home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / nsSafebrowsingApplication.js < prev    next >
Text File  |  2007-10-18  |  148KB  |  4,034 lines

  1. const Cc = Components.classes;
  2. const Ci = Components.interfaces;
  3.  
  4. // This is copied from toolkit/components/content/js/lang.js.
  5. // It seems cleaner to copy this rather than #include from so far away.
  6. Function.prototype.inherits = function(parentCtor) {
  7.   var tempCtor = function(){};
  8.   tempCtor.prototype = parentCtor.prototype;
  9.   this.superClass_ = parentCtor.prototype;
  10.   this.prototype = new tempCtor();
  11. }  
  12.  
  13. /* ***** BEGIN LICENSE BLOCK *****
  14.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  15.  *
  16.  * The contents of this file are subject to the Mozilla Public License Version
  17.  * 1.1 (the "License"); you may not use this file except in compliance with
  18.  * the License. You may obtain a copy of the License at
  19.  * http://www.mozilla.org/MPL/
  20.  *
  21.  * Software distributed under the License is distributed on an "AS IS" basis,
  22.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  23.  * for the specific language governing rights and limitations under the
  24.  * License.
  25.  *
  26.  * The Original Code is Google Safe Browsing.
  27.  *
  28.  * The Initial Developer of the Original Code is Google Inc.
  29.  * Portions created by the Initial Developer are Copyright (C) 2006
  30.  * the Initial Developer. All Rights Reserved.
  31.  *
  32.  * Contributor(s):
  33.  *   Fritz Schneider <fritz@google.com> (original author)
  34.  *
  35.  * Alternatively, the contents of this file may be used under the terms of
  36.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  37.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  38.  * in which case the provisions of the GPL or the LGPL are applicable instead
  39.  * of those above. If you wish to allow use of your version of this file only
  40.  * under the terms of either the GPL or the LGPL, and not to allow others to
  41.  * use your version of this file under the terms of the MPL, indicate your
  42.  * decision by deleting the provisions above and replace them with the notice
  43.  * and other provisions required by the GPL or the LGPL. If you do not delete
  44.  * the provisions above, a recipient may use your version of this file under
  45.  * the terms of any one of the MPL, the GPL or the LGPL.
  46.  *
  47.  * ***** END LICENSE BLOCK ***** */
  48.  
  49.  
  50. // This file implements an event registrar, an object with which you
  51. // can register handlers for arbitrary programmer-defined
  52. // events. Events are arbitrary strings and listeners are functions
  53. // taking an object (stuffed with arguments) as a parameter. When you
  54. // fire an event through the registrar, all listeners are invoked in
  55. // an unspecified order. The firing function takes an object to be
  56. // passed into each handler (it is _not_ copied, so be careful). We
  57. // chose this calling convention so we don't have to change handler
  58. // signatures when adding new information.
  59. //
  60. // Why not just use notifier/observers? Because passing data around
  61. // with them requires either serialization or a new xpcom interface,
  62. // both of which are a pain in the ass.
  63. //
  64. // Example:
  65. //
  66. // // Set up a listener
  67. // this.handleTabload = function(e) {
  68. //   foo(e.url);
  69. //   bar(e.browser);
  70. // };
  71. //
  72. // // Set up the registrar
  73. // var eventTypes = ["tabload", "tabunload", "tabswitch"];
  74. // var registrar = new EventRegistrar(eventTypes);
  75. // var handler = BindToObject(this.handleTabload, this);
  76. //
  77. // // Register a listener
  78. // registrar.registerListener("tabload", handler);
  79. //
  80. // // Fire an event and remove the listener
  81. // var event = { "url": "http://www", "browser": browser };
  82. // registrar.fire("tabload", event);
  83. // registrar.removeListener("tabload", handler);
  84. //
  85. // TODO: could add ability to cancel further handlers by having listeners
  86. //       return a boolean
  87.  
  88. /**
  89.  * EventRegistrars are used to manage user-defined events. 
  90.  *
  91.  * @constructor
  92.  * @param eventTypes {Array or Object} Array holding names of events or
  93.  *                   Object holding properties the values of which are 
  94.  *                   names (strings) for which listeners can register
  95.  */
  96. function EventRegistrar(eventTypes) {
  97.   this.eventTypes = [];
  98.   this.listeners_ = {};          // Listener sets, index by event type
  99.  
  100.   if (eventTypes instanceof Array) {
  101.     var events = eventTypes;
  102.   } else if (typeof eventTypes == "object") {
  103.     var events = [];
  104.     for (var e in eventTypes)
  105.       events.push(eventTypes[e]);
  106.   } else {
  107.     throw new Error("Unrecognized init parameter to EventRegistrar");
  108.   }
  109.  
  110.   for (var i = 0; i < events.length; i++) {
  111.     this.eventTypes.push(events[i]);          // Copy in case caller mutates
  112.     this.listeners_[events[i]] = 
  113.       new ListDictionary(events[i] + "Listeners");
  114.   }
  115. }
  116.  
  117. /**
  118.  * Indicates whether the given event is one the registrar can handle.
  119.  * 
  120.  * @param eventType {String} The name of the event to look up
  121.  * @returns {Boolean} false if the event type is not known or the 
  122.  *          event type string itself if it is
  123.  */
  124. EventRegistrar.prototype.isKnownEventType = function(eventType) {
  125.   for (var i=0; i < this.eventTypes.length; i++)
  126.     if (eventType == this.eventTypes[i])
  127.       return eventType;
  128.   return false;
  129. }
  130.  
  131. /**
  132.  * Add an event type to listen for.
  133.  * @param eventType {String} The name of the event to add
  134.  */
  135. EventRegistrar.prototype.addEventType = function(eventType) {
  136.   if (this.isKnownEventType(eventType))
  137.     throw new Error("Event type already known: " + eventType);
  138.  
  139.   this.eventTypes.push(eventType);
  140.   this.listeners_[eventType] = new ListDictionary(eventType + "Listeners");
  141. }
  142.  
  143. /**
  144.  * Register to receive events of the type passed in. 
  145.  * 
  146.  * @param eventType {String} indicating the event type (one of this.eventTypes)
  147.  * @param listener {Function} to invoke when the event occurs.
  148.  */
  149. EventRegistrar.prototype.registerListener = function(eventType, listener) {
  150.   if (this.isKnownEventType(eventType) === false)
  151.     throw new Error("Unknown event type: " + eventType);
  152.  
  153.   this.listeners_[eventType].addMember(listener);
  154. }
  155.  
  156. /**
  157.  * Unregister a listener.
  158.  * 
  159.  * @param eventType {String} One of EventRegistrar.eventTypes' members
  160.  * @param listener {Function} Function to remove as listener
  161.  */
  162. EventRegistrar.prototype.removeListener = function(eventType, listener) {
  163.   if (this.isKnownEventType(eventType) === false)
  164.     throw new Error("Unknown event type: " + eventType);
  165.  
  166.   this.listeners_[eventType].removeMember(listener);
  167. }
  168.  
  169. /**
  170.  * Invoke the handlers for the given eventType. 
  171.  *
  172.  * @param eventType {String} The event to fire
  173.  * @param e {Object} Object containing the parameters of the event
  174.  */
  175. EventRegistrar.prototype.fire = function(eventType, e) {
  176.   if (this.isKnownEventType(eventType) === false)
  177.     throw new Error("Unknown event type: " + eventType);
  178.  
  179.   var invoke = function(listener) {
  180.     listener(e);
  181.   };
  182.  
  183.   this.listeners_[eventType].forEach(invoke);
  184. }
  185. /* ***** BEGIN LICENSE BLOCK *****
  186.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  187.  *
  188.  * The contents of this file are subject to the Mozilla Public License Version
  189.  * 1.1 (the "License"); you may not use this file except in compliance with
  190.  * the License. You may obtain a copy of the License at
  191.  * http://www.mozilla.org/MPL/
  192.  *
  193.  * Software distributed under the License is distributed on an "AS IS" basis,
  194.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  195.  * for the specific language governing rights and limitations under the
  196.  * License.
  197.  *
  198.  * The Original Code is Google Safe Browsing.
  199.  *
  200.  * The Initial Developer of the Original Code is Google Inc.
  201.  * Portions created by the Initial Developer are Copyright (C) 2006
  202.  * the Initial Developer. All Rights Reserved.
  203.  *
  204.  * Contributor(s):
  205.  *   Fritz Schneider <fritz@google.com> (original author)
  206.  *
  207.  * Alternatively, the contents of this file may be used under the terms of
  208.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  209.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  210.  * in which case the provisions of the GPL or the LGPL are applicable instead
  211.  * of those above. If you wish to allow use of your version of this file only
  212.  * under the terms of either the GPL or the LGPL, and not to allow others to
  213.  * use your version of this file under the terms of the MPL, indicate your
  214.  * decision by deleting the provisions above and replace them with the notice
  215.  * and other provisions required by the GPL or the LGPL. If you do not delete
  216.  * the provisions above, a recipient may use your version of this file under
  217.  * the terms of any one of the MPL, the GPL or the LGPL.
  218.  *
  219.  * ***** END LICENSE BLOCK ***** */
  220.  
  221.  
  222. // This file implements a Dictionary data structure using a list
  223. // (array). We could instead use an object, but using a list enables
  224. // us to have ordering guarantees for iterators. The interface it exposes 
  225. // is:
  226. // 
  227. // addMember(item)
  228. // removeMember(item)
  229. // isMember(item)
  230. // forEach(func)
  231. //
  232. // TODO: this class isn't really a Dictionary, it's more like a 
  233. //       membership set (i.e., a set without union and whatnot). We
  234. //       should probably change the name to avoid confusion.
  235.  
  236. /**
  237.  * Create a new Dictionary data structure.
  238.  *
  239.  * @constructor
  240.  * @param name A string used to name the dictionary
  241.  */
  242. function ListDictionary(name) {
  243.   this.name_ = name;
  244.   this.members_ = [];
  245. }
  246.  
  247. /**
  248.  * Look an item up.
  249.  *
  250.  * @param item An item to look up in the dictionary
  251.  * @returns Boolean indicating if the parameter is a member of the dictionary
  252.  */
  253. ListDictionary.prototype.isMember = function(item) {
  254.   for (var i=0; i < this.members_.length; i++)
  255.     if (this.members_[i] == item)
  256.       return true;
  257.   return false;
  258. }
  259.  
  260. /**
  261.  * Add an item
  262.  *
  263.  * @param item An item to add (does not check for dups)
  264.  */
  265. ListDictionary.prototype.addMember = function(item) {
  266.   this.members_.push(item);
  267. }
  268.  
  269. /**
  270.  * Remove an item
  271.  *
  272.  * @param item The item to remove (doesn't check for dups)
  273.  * @returns Boolean indicating if the item was removed
  274.  */
  275. ListDictionary.prototype.removeMember = function(item) {
  276.   for (var i=0; i < this.members_.length; i++) {
  277.     if (this.members_[i] == item) {
  278.       for (var j=i; j < this.members_.length; j++)
  279.         this.members_[j] = this.members_[j+1];
  280.  
  281.       this.members_.length--;
  282.       return true;
  283.     }
  284.   }
  285.   return false;
  286. }
  287.  
  288. /**
  289.  * Apply a function to each of the members. Does NOT replace the members
  290.  * in the dictionary with results -- it just calls the function on each one.
  291.  *
  292.  * @param func Function to apply to the dictionary's members
  293.  */
  294. ListDictionary.prototype.forEach = function(func) {
  295.   if (typeof func != "function")
  296.     throw new Error("argument to forEach is not a function, it's a(n) " + 
  297.                     typeof func);
  298.  
  299.   for (var i=0; i < this.members_.length; i++)
  300.     func(this.members_[i]);
  301. }
  302. /* ***** BEGIN LICENSE BLOCK *****
  303.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  304.  *
  305.  * The contents of this file are subject to the Mozilla Public License Version
  306.  * 1.1 (the "License"); you may not use this file except in compliance with
  307.  * the License. You may obtain a copy of the License at
  308.  * http://www.mozilla.org/MPL/
  309.  *
  310.  * Software distributed under the License is distributed on an "AS IS" basis,
  311.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  312.  * for the specific language governing rights and limitations under the
  313.  * License.
  314.  *
  315.  * The Original Code is Google Safe Browsing.
  316.  *
  317.  * The Initial Developer of the Original Code is Google Inc.
  318.  * Portions created by the Initial Developer are Copyright (C) 2006
  319.  * the Initial Developer. All Rights Reserved.
  320.  *
  321.  * Contributor(s):
  322.  *   Fritz Schneider <fritz@google.com> (original author)
  323.  *
  324.  * Alternatively, the contents of this file may be used under the terms of
  325.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  326.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  327.  * in which case the provisions of the GPL or the LGPL are applicable instead
  328.  * of those above. If you wish to allow use of your version of this file only
  329.  * under the terms of either the GPL or the LGPL, and not to allow others to
  330.  * use your version of this file under the terms of the MPL, indicate your
  331.  * decision by deleting the provisions above and replace them with the notice
  332.  * and other provisions required by the GPL or the LGPL. If you do not delete
  333.  * the provisions above, a recipient may use your version of this file under
  334.  * the terms of any one of the MPL, the GPL or the LGPL.
  335.  *
  336.  * ***** END LICENSE BLOCK ***** */
  337.  
  338.  
  339. // This file implements a G_TabbedBrowserWatcher, an object
  340. // encapsulating and abstracting the mechanics of working with tabs
  341. // and the documents within them. The watcher provides notification of
  342. // various DOM-related events (a document loaded, a document unloaded,
  343. // tab was created/destroyed, user switched tabs, etc.) as well as
  344. // commonly required methods (get me the current tab, find the tab to
  345. // which this document belongs, etc.).
  346. //
  347. // This class does not do progresslistener-based notifications; for that,
  348. // use the NavWatcher.
  349. //
  350. // Note: I use "browser" and "tab" interchangeably.
  351. //
  352. // This class adds a level of indirection to event registration. You
  353. // initialize it with a tabbedbrowser, and then register to hear
  354. // events from it instead of from the tabbedbrowser or browser itself.
  355. // Your handlers are invoked with a custom object as an argument (see
  356. // below). This object contains useful information such as a reference
  357. // to the browser in which the event is happening and whether the
  358. // event is occurring on the top-level document in that browser.
  359. //
  360. // The events you can register to hear are:
  361. //
  362. // EVENT             DESCRIPTION
  363. // -----             -----------
  364. // load              Fires when a page is shown in the browser window and
  365. //                   this page wasn't in the bfcache
  366. //
  367. // unload            Fires when a page is nav'd away from in the browser window
  368. //                   and the page isn't going into the bfcache
  369. //
  370. // pageshow          Fires when a page is shown in the browser window, whether
  371. //                   it came from bfcache or not. (There is a "persisted"
  372. //                   property we can get from the event object if we'd like.
  373. //                   It indicates whether the page was loaded from bfcache.
  374. //                   If false then we known load recently fired).
  375. //
  376. // pagehide          Fires when a page is nav'd away from in the browser,
  377. //                   whether its going into the bfcache or not. (There is
  378. //                   a persisted property here as well that we're not
  379. //                   propagating -- when its true the page is going into
  380. //                   the bfcache, else it's not, and unload will shortly
  381. //                   fire).
  382. //
  383. // domcontentloaded  BROKEN BROKEN BROKEN BROKEN BROKEN BROKEN BROKEN BROKEN
  384. //                   Fires when a doc's DOM is ready, but its externally linked
  385. //                   content hasn't necessarily loaded. This event is
  386. //                   currently broken: it doesn't fire when using the
  387. //                   forward/back buttons in conjunction with the bfcache.
  388. //                   Bryner is working on a fix.
  389. //
  390. // tabload           Fires when a new tab has been created (but doesn't
  391. //                   necessarily have content loaded in it)
  392. //
  393. // tabunload         Fires when a tab is being destroyed (and might have had
  394. //                   the content it contains destroyed)
  395. //
  396. // tabswitch         Fires when the user switches tabs
  397. //
  398. // tabmove           Fires when a user drags a tab to a different position
  399. //
  400. //
  401. // For pageshow, pagehide, load, unload, and domcontentloaded, the event
  402. // object you'll receive has the following properties:
  403. //
  404. //      doc -- reference to Document on which the event fired
  405. //      browser -- the browser in which the document resides
  406. //      isTop -- boolean indicating if it is the top-level document
  407. //      inSelected -- boolean indicating if it is in the currently selected tab
  408. //
  409. // For tabload and unload it has:
  410. //
  411. //      browser -- reference to the browser that is loading or closing
  412. //
  413. // For tabswitch it has:
  414. //
  415. //      fromBrowser -- reference to the browser user is switching from
  416. //      toBrowser -- reference to the browser user is switching to
  417. //
  418. // For tabmove it has:
  419. //
  420. //      tab -- the tab that was moved (Note that this is the actual
  421. //             tab widget that holds the document title, not the
  422. //             browser object.  We use this because it's the target
  423. //             of the DOMNodeInserted event.)
  424. //      fromIndex -- the tab index before the move
  425. //      toIndex -- the tab index after the move
  426. //
  427. //
  428. // The order of events is:                   tabload
  429. //                                           domcontentloaded
  430. //                                           load
  431. //                                           pageshow
  432. //                                           --  --
  433. //                                           pagehide
  434. //                                           unload
  435. //                                           tabunload
  436. //
  437. // Example:
  438. //
  439. // function handler(e /*event object*/) {
  440. //   foo(e.browser);
  441. // };
  442. // var watcher = new G_TabbedBrowserWatcher(document.getElementById(gBrowser));
  443. // watcher.registerListener("load", handler);      // register for events
  444. // watcher.removeListener("load", handler);        // unregister
  445. //
  446. //
  447. // TODO, BUGS, ISSUES, COMPLICATIONS:
  448. //
  449. // The only major problem is in closing tabs:
  450. //
  451. //     + When you close a tab, the reference to the Document you get in the
  452. //       unload event is undefined. We pass this along. This could easily
  453. //       be fixed by not listening for unload at all, but instead inferring
  454. //       it from the information in pagehide, and then firing our own "fake"
  455. //       unload after firing pagehide.
  456. //
  457. //     + There's no docshell during the pagehide event, so we can't determine
  458. //       if the document is top-level. We pass undefined in this case.
  459. //
  460. //     + Though the browser reference during tabunload will be valid, its
  461. //       members most likely have already been torn down. Use it in an
  462. //       objectsafemap to keep state if you need its members.
  463. //
  464. //     + The event listener DOMNodeInserted has the potential to cause
  465. //       performance problems if there are too many events fired.  It
  466. //       should be ok, since we inserted it as far as possible into
  467. //       the xul tree.
  468. //
  469. //
  470. // TODO: need better enforcement of unique names. Two tabbedbrowserwatchers
  471. //       with the same name will clobber each other because they use that
  472. //       name to mark browsers they've seen.
  473. // 
  474. // TODO: the functions that iterate of windows and documents badly need
  475. //       to be made cleaner. Right now we have multiple implementations
  476. //       that essentially do the same thing :(
  477. //
  478. // But good enough for government work.
  479.  
  480. /**
  481.  * Encapsulates tab-related information. You can use the
  482.  * G_TabbedBrowserWatcher to watch for events on tabs as well as to
  483.  * retrieve tab-related data (such as what tab is currently showing).
  484.  * It receives many event notifications from G_BrowserWatchers it
  485.  * attaches to newly opening tabs.
  486.  *
  487.  * @param tabBrowser A reference to the tabbed browser you wish to watch.
  488.  *
  489.  * @param name String containing a probabilistically unique name. Used to
  490.  *             ensure that each tabbedbrowserwatcher can uniquely mark
  491.  *             browser it has "seen."
  492.  *
  493.  * @param opt_filterAboutBlank Boolean indicating whether to filter events
  494.  *                             for about:blank. These events are often
  495.  *                             spurious since about:blank is the default
  496.  *                             page for an empty browser.
  497.  *
  498.  * @constructor
  499.  */
  500.  
  501. function G_TabbedBrowserWatcher(tabBrowser, name, opt_filterAboutBlank) {
  502.   this.debugZone = "tabbedbrowserwatcher";
  503.   this.registrar_ = new EventRegistrar(G_TabbedBrowserWatcher.events);
  504.   this.tabBrowser_ = tabBrowser;
  505.   this.filterAboutBlank_ = !!opt_filterAboutBlank;
  506.   this.events = G_TabbedBrowserWatcher.events;      // Convenience pointer
  507.  
  508.   // We need some way to tell if we've seen a browser before, so we
  509.   // set a property on it with a probabilistically unique string. The
  510.   // string is a combination of a static string and one passed in by
  511.   // the caller.
  512.   G_Assert(this, typeof name == "string" && !!name,
  513.            "Need a probabilistically unique name");
  514.   this.name_ = name;
  515.   this.mark_ = G_TabbedBrowserWatcher.mark_ + "-" + this.name_;
  516.  
  517.   this.tabbox_ = this.getTabBrowser().mTabBox;
  518.  
  519.   // There's no tabswitch event in Firefox, so we fake it by watching
  520.   // for selects on the tabbox.
  521.   this.onTabSwitchClosure_ = BindToObject(this.onTabSwitch, this);
  522.   this.tabbox_.addEventListener("select",
  523.                                 this.onTabSwitchClosure_, true);
  524.  
  525.   // Used to determine when the user has switched tabs
  526.   this.lastTab_ = this.getCurrentBrowser();
  527. }
  528.  
  529. // Events for which listeners can register
  530. G_TabbedBrowserWatcher.events = {
  531.    TABSWITCH: "tabswitch",
  532.    };
  533.  
  534. // We mark new tabs as we see them
  535. G_TabbedBrowserWatcher.mark_ = "watcher-marked";
  536.  
  537. /**
  538.  * Remove all the event handlers and clean up circular refs.
  539.  */
  540. G_TabbedBrowserWatcher.prototype.shutdown = function() {
  541.   G_Debug(this, "Removing event listeners");
  542.   if (this.tabbox_) {
  543.     this.tabbox_.removeEventListener("select",
  544.                                      this.onTabSwitchClosure_, true);
  545.     // Break circular ref so we can be gc'ed.
  546.     this.tabbox_ = null;
  547.   }
  548.   // Break circular ref so we can be gc'ed.
  549.   if (this.lastTab_) {
  550.     this.lastTab_ = null;
  551.   }
  552.  
  553.   if (this.tabBrowser_) {
  554.     this.tabBrowser_ = null;
  555.   }
  556. }
  557.  
  558. /**
  559.  * Check to see if we've seen a browser before
  560.  *
  561.  * @param browser Browser to check
  562.  * @returns Boolean indicating if we've attached a BrowserWatcher to this
  563.  *          browser
  564.  */
  565. G_TabbedBrowserWatcher.prototype.isInstrumented_ = function(browser) {
  566.   return !!browser[this.mark_];
  567. }
  568.  
  569. /**
  570.  * Attaches a BrowserWatcher to a browser and marks it as seen
  571.  *
  572.  * @param browser Browser to which to attach a G_BrowserWatcher
  573.  */
  574. G_TabbedBrowserWatcher.prototype.instrumentBrowser_ = function(browser) {
  575.   G_Assert(this, !this.isInstrumented_(browser),
  576.            "Browser already instrumented!");
  577.  
  578.   // The browserwatcher will hook itself into the browser and its parent (us)
  579.   new G_BrowserWatcher(this, browser);
  580.   browser[this.mark_] = true;
  581. }
  582.  
  583. /**
  584.  * Register to receive events of a particular type
  585.  *
  586.  * @param eventType String indicating the event (see
  587.  *                  G_TabbedBrowserWatcher.events)
  588.  * @param listener Function to invoke when the event occurs. See top-
  589.  *                 level comments for parameters.
  590.  */
  591. G_TabbedBrowserWatcher.prototype.registerListener = function(eventType,
  592.                                                              listener) {
  593.   this.registrar_.registerListener(eventType, listener);
  594. }
  595.  
  596. /**
  597.  * Unregister a listener.
  598.  *
  599.  * @param eventType String one of G_TabbedBrowserWatcher.events' members
  600.  * @param listener Function to remove as listener
  601.  */
  602. G_TabbedBrowserWatcher.prototype.removeListener = function(eventType,
  603.                                                            listener) {
  604.   this.registrar_.removeListener(eventType, listener);
  605. }
  606.  
  607. /**
  608.  * Send an event to all listeners for that type.
  609.  *
  610.  * @param eventType String indicating the event to trigger
  611.  * @param e Object to pass to each listener (NOT copied -- be careful)
  612.  */
  613. G_TabbedBrowserWatcher.prototype.fire = function(eventType, e) {
  614.   this.registrar_.fire(eventType, e);
  615. }
  616.  
  617. /**
  618.  * Convenience function to send a document-related event. We use this
  619.  * convenience function because the event constructing logic and
  620.  * parameters are the same for all these events. (Document-related
  621.  * events are load, unload, pagehide, pageshow, and domcontentloaded).
  622.  *
  623.  * @param eventType String indicating the type of event to fire (one of
  624.  *                  the document-related events)
  625.  *
  626.  * @param doc Reference to the HTMLDocument the event is occuring to
  627.  *
  628.  * @param browser Reference to the browser in which the document is contained
  629.  */
  630. G_TabbedBrowserWatcher.prototype.fireDocEvent_ = function(eventType,
  631.                                                           doc,
  632.                                                           browser) {
  633.   // If we've already shutdown, don't bother firing any events.
  634.   if (!this.tabBrowser_) {
  635.     G_Debug(this, "Firing event after shutdown: " + eventType);
  636.     return;
  637.   }
  638.  
  639.   try {
  640.     // Could be that the browser's contentDocument has already been torn
  641.     // down. If so, this throws, and we can't tell without keeping more
  642.     // state whether doc was the top frame.
  643.     var isTop = (doc == browser.contentDocument);
  644.   } catch(e) {
  645.     var isTop = undefined;
  646.   }
  647.  
  648.   var inSelected = (browser == this.getCurrentBrowser());
  649.  
  650.   var location = doc ? doc.location.href : undefined;
  651.  
  652.   // Only send notifications for about:config's if we're supposed to
  653.   if (!this.filterAboutBlank_ || location != "about:blank") {
  654.  
  655.     G_Debug(this, "firing " + eventType + " for " + location +
  656.             (isTop ? " (isTop)" : "") + (inSelected ? " (inSelected)" : ""));
  657.     this.fire(eventType, { "doc": doc,
  658.                            "isTop": isTop,
  659.                            "inSelected": inSelected,
  660.                            "browser": browser});
  661.   }
  662. }
  663.  
  664. /**
  665.  * Invoked when the user might have switched tabs
  666.  *
  667.  * @param e Event object
  668.  */
  669. G_TabbedBrowserWatcher.prototype.onTabSwitch = function(e) {
  670.   // Filter spurious events
  671.   // The event target is usually tabs but can be tabpanels when tabs were opened
  672.   // programatically via tabbrowser.addTab().
  673.   if (e.target == null || 
  674.       (e.target.localName != "tabs" && e.target.localName != "tabpanels"))
  675.     return;
  676.  
  677.   var fromBrowser = this.lastTab_;
  678.   var toBrowser = this.getCurrentBrowser();
  679.  
  680.   if (fromBrowser != toBrowser) {
  681.     this.lastTab_ = toBrowser;
  682.     G_Debug(this, "firing tabswitch");
  683.     this.fire(this.events.TABSWITCH, { "fromBrowser": fromBrowser,
  684.                                        "toBrowser": toBrowser });
  685.   }
  686. }
  687.  
  688. // Utility functions
  689.  
  690. /**
  691.  * Returns a reference to the tabbed browser this G_TabbedBrowserWatcher
  692.  * was initialized with.
  693.  */
  694. G_TabbedBrowserWatcher.prototype.getTabBrowser = function() {
  695.   return this.tabBrowser_;
  696. }
  697.  
  698. /**
  699.  * Returns a reference to the currently selected tab.
  700.  */
  701. G_TabbedBrowserWatcher.prototype.getCurrentBrowser = function() {
  702.   return this.getTabBrowser().selectedBrowser;
  703. }
  704.  
  705. /**
  706.  * Returns a reference to the top window in the currently selected tab.
  707.  */
  708. G_TabbedBrowserWatcher.prototype.getCurrentWindow = function() {
  709.   return this.getCurrentBrowser().contentWindow;
  710. }
  711.  
  712. /**
  713.  * Find the browser corresponding to a Document
  714.  *
  715.  * @param doc Document we want the browser for
  716.  * @returns Reference to the browser in which the given document is found
  717.  *          or null if not found
  718.  */
  719. G_TabbedBrowserWatcher.prototype.getBrowserFromDocument = function(doc) {
  720.   // Could instead get the top window of the browser in which the doc
  721.   // is found via doc.defaultView.top, but sometimes the document
  722.   // isn't in a browser at all (it's being unloaded, for example), so
  723.   // defaultView won't be valid.
  724.  
  725.   // Helper: return true if doc is a sub-document of win
  726.   function docInWindow(doc, win) {
  727.     if (win.document == doc)
  728.       return true;
  729.  
  730.     if (win.frames)
  731.       for (var i = 0; i < win.frames.length; i++)
  732.         if (docInWindow(doc, win.frames[i]))
  733.           return true;
  734.  
  735.     return false;
  736.   }
  737.  
  738.   var browsers = this.getTabBrowser().browsers;
  739.   for (var i = 0; i < browsers.length; i++)
  740.     if (docInWindow(doc, browsers[i].contentWindow))
  741.       return browsers[i];
  742.  
  743.   return null;
  744. }
  745.  
  746. /**
  747.  * Find the Document that has the given URL loaded. Returns on the
  748.  * _first_ such document found, so be careful.
  749.  *
  750.  * TODO make doc/window searches more elegant, and don't use inner functions
  751.  *
  752.  * @param url String indicating the URL we're searching for
  753.  * @param opt_browser Optional reference to a browser. If given, the
  754.  *                    search will be confined to only this browser.
  755.  * @returns Reference to the Document with that URL or null if not found
  756.  */
  757. G_TabbedBrowserWatcher.prototype.getDocumentFromURL = function(url,
  758.                                                                opt_browser) {
  759.  
  760.   // Helper function: return the Document in win that has location of url
  761.   function docWithURL(win, url) {
  762.     if (win.document.location.href == url)
  763.       return win.document;
  764.  
  765.     if (win.frames)
  766.       for (var i = 0; i < win.frames.length; i++) {
  767.         var rv = docWithURL(win.frames[i], url);
  768.       if (rv)
  769.           return rv;
  770.       }
  771.  
  772.     return null;
  773.   }
  774.  
  775.   if (opt_browser)
  776.     return docWithURL(opt_browser.contentWindow, url);
  777.  
  778.   var browsers = this.getTabBrowser().browsers;
  779.   for (var i=0; i < browsers.length; i++) {
  780.     var rv = docWithURL(browsers[i].contentWindow, url);
  781.     if (rv)
  782.       return rv;
  783.   }
  784.  
  785.   return null;
  786. }
  787.  
  788. /**
  789.  * Find the all Documents that have the given URL loaded.
  790.  *
  791.  * TODO make doc/window searches more elegant, and don't use inner functions
  792.  *
  793.  * @param url String indicating the URL we're searching for
  794.  *
  795.  * @param opt_browser Optional reference to a browser. If given, the
  796.  *                    search will be confined to only this browser.
  797.  *
  798.  * @returns Array of Documents with the given URL (zero length if none found)
  799.  */
  800. G_TabbedBrowserWatcher.prototype.getDocumentsFromURL = function(url,
  801.                                                                 opt_browser) {
  802.  
  803.   var docs = [];
  804.  
  805.   // Helper function: add Docs in win with the location of url
  806.   function getDocsWithURL(win, url) {
  807.     if (win.document.location.href == url)
  808.       docs.push(win.document);
  809.  
  810.     if (win.frames)
  811.       for (var i = 0; i < win.frames.length; i++)
  812.         getDocsWithURL(win.frames[i], url);
  813.   }
  814.  
  815.   if (opt_browser)
  816.     return getDocsWithURL(opt_browser.contentWindow, url);
  817.  
  818.   var browsers = this.getTabBrowser().browsers;
  819.   for (var i=0; i < browsers.length; i++)
  820.     getDocsWithURL(browsers[i].contentWindow, url);
  821.  
  822.   return docs;
  823. }
  824.  
  825.  
  826. /**
  827.  * Finds the browser in which a Window resides.
  828.  *
  829.  * @param sub Window to find
  830.  * @returns Reference to the browser in which sub resides, else null
  831.  *          if not found
  832.  */
  833. G_TabbedBrowserWatcher.prototype.getBrowserFromWindow = function(sub) {
  834.  
  835.   // Helpfer function: return true if sub is a sub-window of win
  836.   function containsSubWindow(sub, win) {
  837.     if (win == sub)
  838.       return true;
  839.  
  840.     if (win.frames)
  841.       for (var i=0; i < win.frames.length; i++)
  842.         if (containsSubWindow(sub, win.frames[i]))
  843.           return true;
  844.  
  845.     return false;
  846.   }
  847.  
  848.   var browsers = this.getTabBrowser().browsers;
  849.   for (var i=0; i < browsers.length; i++)
  850.     if (containsSubWindow(sub, browsers[i].contentWindow))
  851.       return browsers[i];
  852.  
  853.   return null;
  854. }
  855.  
  856. /**
  857.  * Finds the XUL <tab> tag corresponding to a given browser.
  858.  *
  859.  * @param tabBrowser Reference to the tabbed browser in which browser lives
  860.  * @param browser Reference to the browser we wish to find the tab of
  861.  * @returns Reference to the browser's tab element, or null
  862.  * @static
  863.  */
  864. G_TabbedBrowserWatcher.getTabElementFromBrowser = function(tabBrowser,
  865.                                                            browser) {
  866.  
  867.   for (var i=0; i < tabBrowser.browsers.length; i++)
  868.     if (tabBrowser.browsers[i] == browser)
  869.       return tabBrowser.mTabContainer.childNodes[i];
  870.  
  871.   return null;
  872. }
  873. //@line 16 "/cygdrive/K/tinderbuild/src/flock/mozilla/browser/components/safebrowsing/src/nsSafebrowsingApplication.js"
  874.  
  875. /* ***** BEGIN LICENSE BLOCK *****
  876.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  877.  *
  878.  * The contents of this file are subject to the Mozilla Public License Version
  879.  * 1.1 (the "License"); you may not use this file except in compliance with
  880.  * the License. You may obtain a copy of the License at
  881.  * http://www.mozilla.org/MPL/
  882.  *
  883.  * Software distributed under the License is distributed on an "AS IS" basis,
  884.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  885.  * for the specific language governing rights and limitations under the
  886.  * License.
  887.  *
  888.  * The Original Code is Google Safe Browsing.
  889.  *
  890.  * The Initial Developer of the Original Code is Google Inc.
  891.  * Portions created by the Initial Developer are Copyright (C) 2006
  892.  * the Initial Developer. All Rights Reserved.
  893.  *
  894.  * Contributor(s):
  895.  *   Fritz Schneider <fritz@google.com> (original author)
  896.  *
  897.  * Alternatively, the contents of this file may be used under the terms of
  898.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  899.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  900.  * in which case the provisions of the GPL or the LGPL are applicable instead
  901.  * of those above. If you wish to allow use of your version of this file only
  902.  * under the terms of either the GPL or the LGPL, and not to allow others to
  903.  * use your version of this file under the terms of the MPL, indicate your
  904.  * decision by deleting the provisions above and replace them with the notice
  905.  * and other provisions required by the GPL or the LGPL. If you do not delete
  906.  * the provisions above, a recipient may use your version of this file under
  907.  * the terms of any one of the MPL, the GPL or the LGPL.
  908.  *
  909.  * ***** END LICENSE BLOCK ***** */
  910.  
  911. // We instantiate this variable when we create the application.
  912. var gDataProvider = null;
  913.  
  914. // An instance of our application is a PROT_Application object. It
  915. // basically just populates a few globals and instantiates wardens and
  916. // the listmanager.
  917.  
  918. /**
  919.  * An instance of our application. There should be exactly one of these.
  920.  * 
  921.  * Note: This object should instantiated only at profile-after-change
  922.  * or later because the listmanager and the cryptokeymanager need to
  923.  * read and write data files. Additionally, NSS isn't loaded until
  924.  * some time around then (Moz bug #321024).
  925.  *
  926.  * @constructor
  927.  */
  928. function PROT_Application() {
  929.   this.debugZone= "application";
  930.  
  931. //@line 88 "/cygdrive/K/tinderbuild/src/flock/mozilla/browser/components/safebrowsing/src/../content/application.js"
  932.   
  933.   // expose some classes
  934.   this.G_TabbedBrowserWatcher = G_TabbedBrowserWatcher;
  935.   this.PROT_Controller = PROT_Controller;
  936.   this.PROT_PhishingWarden = PROT_PhishingWarden;
  937.  
  938.   // Load data provider pref values
  939.   gDataProvider = new PROT_DataProvider();
  940.  
  941.   // expose the object
  942.   this.wrappedJSObject = this;
  943. }
  944.  
  945. /**
  946.  * @return String the report phishing URL (localized).
  947.  */
  948. PROT_Application.prototype.getReportPhishingURL = function() {
  949.   return gDataProvider.getReportPhishURL();
  950. }
  951. /* ***** BEGIN LICENSE BLOCK *****
  952.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  953.  *
  954.  * The contents of this file are subject to the Mozilla Public License Version
  955.  * 1.1 (the "License"); you may not use this file except in compliance with
  956.  * the License. You may obtain a copy of the License at
  957.  * http://www.mozilla.org/MPL/
  958.  *
  959.  * Software distributed under the License is distributed on an "AS IS" basis,
  960.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  961.  * for the specific language governing rights and limitations under the
  962.  * License.
  963.  *
  964.  * The Original Code is Google Safe Browsing.
  965.  *
  966.  * The Initial Developer of the Original Code is Google Inc.
  967.  * Portions created by the Initial Developer are Copyright (C) 2006
  968.  * the Initial Developer. All Rights Reserved.
  969.  *
  970.  * Contributor(s):
  971.  *   Fritz Schneider <fritz@google.com> (original author)
  972.  *
  973.  * Alternatively, the contents of this file may be used under the terms of
  974.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  975.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  976.  * in which case the provisions of the GPL or the LGPL are applicable instead
  977.  * of those above. If you wish to allow use of your version of this file only
  978.  * under the terms of either the GPL or the LGPL, and not to allow others to
  979.  * use your version of this file under the terms of the MPL, indicate your
  980.  * decision by deleting the provisions above and replace them with the notice
  981.  * and other provisions required by the GPL or the LGPL. If you do not delete
  982.  * the provisions above, a recipient may use your version of this file under
  983.  * the terms of any one of the MPL, the GPL or the LGPL.
  984.  *
  985.  * ***** END LICENSE BLOCK ***** */
  986.  
  987. // There is one BrowserView per browser window, and each BrowserView
  988. // is responsible for keeping track of problems (phishy documents)
  989. // within that window. The BrowserView is also responsible for
  990. // figuring out what to do about such problems, for example, whether
  991. // the tab with a phishy page is currently showing and therefore if we
  992. // should be showing a warning.
  993. // 
  994. // The BrowserView receives information from three places:
  995. //
  996. // - from the phishing warden. When the phishing warden notices a
  997. //   problem, it queries all browser views to see which one (if any)
  998. //   has the Document that is problematic. It then hands the problem
  999. //   off to the appropriate BrowserView.
  1000. // 
  1001. // - from the controller. The controller responds to explicit user 
  1002. //   actions (tab switches, requests to hide the warning message, 
  1003. //   etc.) and let's the BrowserView know about any user action 
  1004. //   having to do with the problems it is tracking.
  1005. //
  1006. // - from the TabbedBrowserWatcher. When the BrowserView is keeping
  1007. //   track of a problematic document it listens for interesting
  1008. //   events affecting it, for example pagehide (at which point
  1009. //   we presumably hide the warning if we're showing it).
  1010. //
  1011. // The BrowserView associates at most one "problem" with each Document
  1012. // in the browser window. It keeps state about which Documents are 
  1013. // problematic by storing a "problem queue" on each browser (tab).
  1014. // At most one problematic document per browser (tab) is active
  1015. // at any time. That is, we show the warning for at most one phishy
  1016. // document at any one time. If another phishy doc loads in that tab,
  1017. // it goes onto the end of the queue to be activated only when the
  1018. // currently active document goes away.
  1019. //
  1020. // If we had multiple types of warnings (one for after the page had
  1021. // loaded, one for when the user clicked a link, etc) here's where
  1022. // we'd select the appropate one to use. As it stands, we only have
  1023. // one displayer (an "afterload" displayer). A displayer knows _how_
  1024. // to display a warning, whereas as the BrowserView knows _what_ and
  1025. // _when_.
  1026. //
  1027. // To keep things (relatively) easy to reason about and efficient (the
  1028. // phishwarden could be querying us inside a progresslistener
  1029. // notification, or the controller inside an event handler), we have
  1030. // the following rules:
  1031. //
  1032. // - at most one of a displayer's start() or stop() methods is called
  1033. //   in any iteration (if calling two is required, the second is run in 
  1034. //   the next event loop)
  1035. // - displayers should run their operations synchronously so we don't have
  1036. //   to look two places (here and in the displayer) to see what is happening 
  1037. //   when
  1038. // - displayer actions are run after cleaning up the browser view state
  1039. //   in case they have consequences
  1040. //
  1041. // TODO: this could use some redesign, but I don't have time.
  1042. // TODO: the queue needs to be abstracted, but we want another release fast,
  1043. //       so I'm not going to touch it for the time being
  1044. // TODO: IDN issues and canonical URLs?
  1045. // TODO: Perhaps we should blur the page before showing a warning in order
  1046. //       to prevent stray keystrokes?
  1047.  
  1048. /**
  1049.  * The BrowerView is responsible for keeping track of and figuring out
  1050.  * what to do with problems within a single browser window.
  1051.  * 
  1052.  * TODO 
  1053.  * Unify all browser-related state here. Currently it's split
  1054.  * between two objects, this object and the controller. We could have
  1055.  * this object be solely responsible for UI hide/show decisions, which
  1056.  * would probably make it easier to reason about what's going on.
  1057.  * 
  1058.  * TODO 
  1059.  * Investigate an alternative model. For example, we could factor out
  1060.  * the problem signaling stuff from the tab/UI logic into a
  1061.  * ProblemRegistry. Attach listeners to new docs/requests as they go
  1062.  * by and have these listeners periodically check in with a
  1063.  * ProblemRegistry to see if they're watching a problematic
  1064.  * doc/request. If so, then have them flag the browser view to be
  1065.  * aware of the problem.
  1066.  *
  1067.  * @constructor
  1068.  * @param tabWatcher Reference to the TabbedBrowserWatcher we'll use to query 
  1069.  *                   for information about active tabs/browsers.
  1070.  * @param doc Reference to the XUL Document (browser window) in which the 
  1071.  *            tabwatcher is watching
  1072.  */ 
  1073. function PROT_BrowserView(tabWatcher, doc) {
  1074.   this.debugZone = "browserview";
  1075.   this.tabWatcher_ = tabWatcher;
  1076.   this.doc_ = doc;
  1077. }
  1078.  
  1079. /**
  1080.  * See if we have any Documents with a given (problematic) URL that
  1081.  * haven't yet been marked as problems. Called as a subroutine by
  1082.  * tryToHandleProblemRequest().
  1083.  *
  1084.  * @param url String containing the URL to look for
  1085.  *
  1086.  * @returns Reference to an unhandled Document with the problem URL or null
  1087.  */
  1088. PROT_BrowserView.prototype.getFirstUnhandledDocWithURL_ = function(url) {
  1089.   var docs = this.tabWatcher_.getDocumentsFromURL(url);
  1090.   if (!docs.length)
  1091.     return null;
  1092.  
  1093.   for (var i = 0; i < docs.length; i++) {
  1094.     // We only care about top level documents (i.e., we don't care about
  1095.     // frames).
  1096.     if (docs[i].defaultView.top != docs[i].defaultView)
  1097.       continue;
  1098.  
  1099.     var browser = this.tabWatcher_.getBrowserFromDocument(docs[i]);
  1100.     G_Assert(this, !!browser, "Found doc but can't find browser???");
  1101.     var alreadyHandled = this.getProblem_(docs[i], browser);
  1102.  
  1103.     if (!alreadyHandled)
  1104.       return docs[i];
  1105.   }
  1106.   return null;
  1107. }
  1108.  
  1109. /**
  1110.  * Invoked by the warden to give us the opportunity to handle a
  1111.  * problem.  A problem is signaled once per request for a problem
  1112.  * Document and is handled at most once, so there's no issue with us
  1113.  * "losing" a problem due to multiple concurrently loading Documents
  1114.  * with the same URL.
  1115.  *
  1116.  * @param warden Reference to the warden signalling the problem. We'll
  1117.  *               need him to instantiate one of his warning displayers
  1118.  * 
  1119.  * @param request The nsIRequest that is problematic
  1120.  *
  1121.  * @returns Boolean indicating whether we handled problem
  1122.  */
  1123. PROT_BrowserView.prototype.tryToHandleProblemRequest = function(warden,
  1124.                                                                 request) {
  1125.  
  1126.   var doc = this.getFirstUnhandledDocWithURL_(request.name);
  1127.   if (doc) {
  1128.     var browser = this.tabWatcher_.getBrowserFromDocument(doc);
  1129.     G_Assert(this, !!browser, "Couldn't get browser from problem doc???");
  1130.     G_Assert(this, !this.getProblem_(doc, browser),
  1131.              "Doc is supposedly unhandled, but has state?");
  1132.     
  1133.     this.isProblemDocument_(browser, doc, warden);
  1134.     return true;
  1135.   }
  1136.   return false;
  1137. }
  1138.  
  1139. /**
  1140.  * We're sure a particular Document is problematic, so let's instantiate
  1141.  * a dispalyer for it and add it to the problem queue for the browser.
  1142.  *
  1143.  * @param browser Reference to the browser in which the problem doc resides
  1144.  *
  1145.  * @param doc Reference to the problematic document
  1146.  * 
  1147.  * @param warden Reference to the warden signalling the problem.
  1148.  */
  1149. PROT_BrowserView.prototype.isProblemDocument_ = function(browser, 
  1150.                                                          doc, 
  1151.                                                          warden) {
  1152.  
  1153.   G_Debug(this, "Document is problem: " + doc.location.href);
  1154.  
  1155.   var url = doc.location.href;
  1156.  
  1157.   // We only have one type of displayer right now
  1158.   var displayer = new warden.displayers_["afterload"]("Phishing afterload",
  1159.                                                       browser,
  1160.                                                       this.doc_,
  1161.                                                       url);
  1162.  
  1163.   // We listen for the problematic document being navigated away from
  1164.   // so we can remove it from the problem queue
  1165.  
  1166.   var hideHandler = BindToObject(this.onNavAwayFromProblem_, 
  1167.                                  this, 
  1168.                                  doc, 
  1169.                                  browser);
  1170.   doc.defaultView.addEventListener("pagehide", hideHandler, true);
  1171.  
  1172.   // More info than we technically need, but it comes in handy for debugging
  1173.   var problem = {
  1174.     "browser_": browser,
  1175.     "doc_": doc,
  1176.     "displayer_": displayer,
  1177.     "url_": url,
  1178.     "hideHandler_": hideHandler,
  1179.   };
  1180.   var numInQueue = this.queueProblem_(browser, problem);
  1181.  
  1182.   // If the queue was empty, schedule us to take something out
  1183.   if (numInQueue == 1)
  1184.     new G_Alarm(BindToObject(this.unqueueNextProblem_, this, browser), 0);
  1185. }
  1186.  
  1187. /**
  1188.  * Invoked when a problematic document is navigated away from. 
  1189.  *
  1190.  * @param doc Reference to the problematic Document navigated away from
  1191.  
  1192.  * @param browser Reference to the browser in which the problem document
  1193.  *                unloaded
  1194.  */
  1195. PROT_BrowserView.prototype.onNavAwayFromProblem_ = function(doc, browser) {
  1196.  
  1197.   G_Debug(this, "User nav'd away from problem.");
  1198.   var problem = this.getProblem_(doc, browser);
  1199.   (new PROT_Reporter).report("phishnavaway", problem.url_);
  1200.  
  1201.   G_Assert(this, doc === problem.doc_, "State doc not equal to nav away doc?");
  1202.   G_Assert(this, browser === problem.browser_, 
  1203.            "State browser not equal to nav away browser?");
  1204.   
  1205.   this.problemResolved(browser, doc);
  1206. }
  1207.  
  1208. /**
  1209.  * @param browser Reference to a browser we'd like to know about
  1210.  * 
  1211.  * @returns Boolean indicating if the browser in question has 
  1212.  *          problematic content
  1213.  */
  1214. PROT_BrowserView.prototype.hasProblem = function(browser) {
  1215.   return this.hasNonemptyProblemQueue_(browser);
  1216. }
  1217.  
  1218. /**
  1219.  * @param browser Reference to a browser we'd like to know about
  1220.  *
  1221.  * @returns Boolean indicating if the browser in question has a
  1222.  *          problem (i.e., it has a non-empty problem queue)
  1223.  */
  1224. PROT_BrowserView.prototype.hasNonemptyProblemQueue_ = function(browser) {
  1225.   try {
  1226.     return !!browser.PROT_problemState__ && 
  1227.       !!browser.PROT_problemState__.length;
  1228.   } catch(e) {
  1229.     // We could be checking a browser that has just been closed, in
  1230.     // which case its properties will not be valid, causing the above
  1231.     // statement to throw an error. Since this case handled elsewhere,
  1232.     // just return false.
  1233.     return false;
  1234.   }
  1235. }
  1236.  
  1237. /**
  1238.  * Invoked to indicate that the problem for a particular problematic
  1239.  * document in a browser has been resolved (e.g., by being navigated
  1240.  * away from).
  1241.  *
  1242.  * @param browser Reference to the browser in which resolution is happening
  1243.  *
  1244.  * @param opt_doc Reference to the problematic doc whose problem was resolved
  1245.  *                (if absent, assumes the doc assocaited with the currently
  1246.  *                active displayer)
  1247.  */
  1248. PROT_BrowserView.prototype.problemResolved = function(browser, opt_doc) {
  1249.   var problem;
  1250.   var doc;
  1251.   if (!!opt_doc) {
  1252.     doc = opt_doc;
  1253.     problem = this.getProblem_(doc, browser);
  1254.   } else {
  1255.     problem = this.getCurrentProblem_(browser);
  1256.     doc = problem.doc_;
  1257.   }
  1258.  
  1259.   problem.displayer_.done();
  1260.   var wasHead = this.deleteProblemFromQueue_(doc, browser);
  1261.  
  1262.   // Peek at the next problem (if any) in the queue for this browser
  1263.   var queueNotEmpty = this.getCurrentProblem_(browser);
  1264.  
  1265.   if (wasHead && queueNotEmpty) {
  1266.     G_Debug(this, "More problems pending. Scheduling unqueue.");
  1267.     new G_Alarm(BindToObject(this.unqueueNextProblem_, this, browser), 0);
  1268.   }
  1269. }
  1270.  
  1271. /**
  1272.  * Peek at the top of the problem queue and if there's something there,
  1273.  * make it active. 
  1274.  *
  1275.  * @param browser Reference to the browser we should activate a problem
  1276.  *                displayer in if one is available
  1277.  */
  1278. PROT_BrowserView.prototype.unqueueNextProblem_ = function(browser) {
  1279.   var problem = this.getCurrentProblem_(browser);
  1280.   if (!problem) {
  1281.     G_Debug(this, "No problem in queue; doc nav'd away from? (shrug)");
  1282.     return;
  1283.   }
  1284.  
  1285.   // Two problem docs that load in rapid succession could both schedule 
  1286.   // themselves to be unqueued before this method is called. So ensure 
  1287.   // that the problem at the head of the queue is not, in fact, active.
  1288.   if (!problem.displayer_.isActive()) {
  1289.  
  1290.     // It could be the case that the server is really slow to respond,
  1291.     // so there might not yet be anything in the problem Document. If
  1292.     // we show the warning when that's the case, the user will see a
  1293.     // blank document greyed out, and if they cancel the dialog
  1294.     // they'll see the page they're navigating away from because it
  1295.     // hasn't been painted over yet (b/c there's no content for the
  1296.     // problem page). So here we ensure that we have content for the
  1297.     // problem page before showing the dialog.
  1298.     var haveContent = false;
  1299.     try {
  1300.       // This will throw if there's no content yet
  1301.       var h = problem.doc_.defaultView.getComputedStyle(problem.doc_.body, "")
  1302.               .getPropertyValue("height");
  1303.       G_Debug(this, "body height: " + h);
  1304.  
  1305.       if (Number(h.substring(0, h.length - 2)))
  1306.         haveContent = true;
  1307.  
  1308.     } catch (e) {
  1309.       G_Debug(this, "Masked in unqueuenextproblem: " + e);
  1310.     }
  1311.     
  1312.     if (!haveContent) {
  1313.  
  1314.       G_Debug(this, "Didn't get computed style. Re-queueing.");
  1315.  
  1316.       // One stuck problem document in a page shouldn't prevent us
  1317.       // warning on other problem frames that might be loading or
  1318.       // loaded. So stick the Document that doesn't have content
  1319.       // back at the end of the queue.
  1320.       var p = this.removeProblemFromQueue_(problem.doc_, browser);
  1321.       G_Assert(this, p === problem, "Unqueued wrong problem?");
  1322.       this.queueProblem_(browser, problem);
  1323.  
  1324.       // Try again in a bit. This opens us up to a potential
  1325.       // vulnerability (put tons of hanging frames in a page
  1326.       // ahead of your real phishy frame), but the risk at the
  1327.       // moment is really low (plus it is outside our threat
  1328.       // model).
  1329.       new G_Alarm(BindToObject(this.unqueueNextProblem_, 
  1330.                                this, 
  1331.                                browser),
  1332.                   200 /*ms*/);
  1333.       return;
  1334.     }
  1335.  
  1336.     problem.displayer_.start();
  1337.  
  1338.     // OK, we have content, but there there is an additional
  1339.     // issue. Due to a bfcache bug, if we show the warning during
  1340.     // paint suppression, the collapsing of the content pane affects
  1341.     // the doc we're naving from :( The symptom is a page with grey
  1342.     // screen on navigation to or from a phishing page (the
  1343.     // contentDocument will have width zero).
  1344.     //
  1345.     // Paint supression lasts at most 250ms from when the parser sees
  1346.     // the body, and the parser sees the body well before it has a
  1347.     // height. We err on the side of caution.
  1348.     //
  1349.     // Thanks to bryner for helping to track the bfcache bug down.
  1350.     // https://bugzilla.mozilla.org/show_bug.cgi?id=319646
  1351.     
  1352.     if (this.tabWatcher_.getCurrentBrowser() === browser)
  1353.       new G_Alarm(BindToObject(this.problemBrowserMaybeSelected, 
  1354.                                this, 
  1355.                                browser),
  1356.                   350 /*ms*/);
  1357.   }
  1358. }
  1359.  
  1360. /**
  1361.  * Helper function that adds a new problem to the queue of problems pending
  1362.  * on this browser.
  1363.  *
  1364.  * @param browser Browser to which we should add state
  1365.  *
  1366.  * @param problem Object (structure, really) encapsulating the problem
  1367.  *
  1368.  * @returns Number indicating the number of items in the queue (and from
  1369.  *          which you can infer whether the recently added item was
  1370.  *          placed at the head, and hence should be active.
  1371.  */
  1372. PROT_BrowserView.prototype.queueProblem_ = function(browser, problem) {
  1373.   G_Debug(this, "Adding problem state for " + problem.url_);
  1374.  
  1375.   if (this.hasNonemptyProblemQueue_(browser))
  1376.     G_Debug(this, "Already has problem state. Queueing this problem...");
  1377.  
  1378.   // First problem ever signaled on this browser? Make a new queue!
  1379.   if (browser.PROT_problemState__ == undefined)
  1380.     browser.PROT_problemState__ = [];
  1381.  
  1382.   browser.PROT_problemState__.push(problem);
  1383.   return browser.PROT_problemState__.length;
  1384. }
  1385.  
  1386. /**
  1387.  * Helper function that removes a problem from the queue and deactivates
  1388.  * it.
  1389.  *
  1390.  * @param doc Reference to the doc for which we should remove state
  1391.  *
  1392.  * @param browser Reference to the browser from which we should remove
  1393.  *                state
  1394.  *
  1395.  * @returns Boolean indicating if the remove problem was currently active
  1396.  *          (that is, if it was at the head of the queue)
  1397.  */
  1398. PROT_BrowserView.prototype.deleteProblemFromQueue_ = function(doc, browser) {
  1399.   G_Debug(this, "Deleting problem state for " + browser);
  1400.   G_Assert(this, !!this.hasNonemptyProblemQueue_(browser),
  1401.            "Browser has no problem state");
  1402.  
  1403.   var problem = this.getProblem_(doc, browser);
  1404.   G_Assert(this, !!problem, "Couldnt find state in removeproblemstate???");
  1405.  
  1406.   var wasHead = browser.PROT_problemState__[0] === problem;
  1407.   this.removeProblemFromQueue_(doc, browser);
  1408.  
  1409.   var hideHandler = problem.hideHandler_;
  1410.   G_Assert(this, !!hideHandler, "No hidehandler in state?");
  1411.   problem.doc_.defaultView.removeEventListener("pagehide",
  1412.                                                hideHandler,
  1413.                                                true);
  1414.   return wasHead;
  1415. }
  1416.  
  1417. /**
  1418.  * Helper function that removes a problem from the queue but does 
  1419.  * NOT deactivate it.
  1420.  *
  1421.  * @param doc Reference to the doc for which we should remove state
  1422.  *
  1423.  * @param browser Reference to the browser from which we should remove
  1424.  *                state
  1425.  *
  1426.  * @returns Boolean indicating if the remove problem was currently active
  1427.  *          (that is, if it was at the head of the queue)
  1428.  */
  1429. PROT_BrowserView.prototype.removeProblemFromQueue_ = function(doc, browser) {
  1430.   G_Debug(this, "Removing problem state for " + browser);
  1431.   G_Assert(this, !!this.hasNonemptyProblemQueue_(browser),
  1432.            "Browser has no problem state");
  1433.  
  1434.   var problem = null;
  1435.   // TODO Blech. Let's please have an abstraction here instead.
  1436.   for (var i = 0; i < browser.PROT_problemState__.length; i++)
  1437.     if (browser.PROT_problemState__[i].doc_ === doc) {
  1438.       problem = browser.PROT_problemState__.splice(i, 1)[0];
  1439.       break;
  1440.     }
  1441.   return problem;
  1442. }
  1443.  
  1444. /**
  1445.  * Retrieve (but do not remove) the problem state for a particular
  1446.  * problematic Document in this browser
  1447.  *
  1448.  * @param doc Reference to the problematic doc to get state for
  1449.  *
  1450.  * @param browser Reference to the browser from which to get state
  1451.  *
  1452.  * @returns Object encapsulating the state we stored, or null if none
  1453.  */
  1454. PROT_BrowserView.prototype.getProblem_ = function(doc, browser) {
  1455.   if (!this.hasNonemptyProblemQueue_(browser))
  1456.     return null;
  1457.  
  1458.   // TODO Blech. Let's please have an abstraction here instead.
  1459.   for (var i = 0; i < browser.PROT_problemState__.length; i++)
  1460.     if (browser.PROT_problemState__[i].doc_ === doc)
  1461.       return browser.PROT_problemState__[i];
  1462.   return null;
  1463. }
  1464.  
  1465. /**
  1466.  * Retrieve the problem state for the currently active problem Document 
  1467.  * in this browser
  1468.  *
  1469.  * @param browser Reference to the browser from which to get state
  1470.  *
  1471.  * @returns Object encapsulating the state we stored, or null if none
  1472.  */
  1473. PROT_BrowserView.prototype.getCurrentProblem_ = function(browser) {
  1474.   return browser.PROT_problemState__[0];
  1475. }
  1476.  
  1477. /**
  1478.  * Invoked by the controller when the user switches tabs away from a problem 
  1479.  * tab. 
  1480.  *
  1481.  * @param browser Reference to the tab that was switched from
  1482.  */
  1483. PROT_BrowserView.prototype.problemBrowserUnselected = function(browser) {
  1484.   var problem = this.getCurrentProblem_(browser);
  1485.   G_Assert(this, !!problem, "Couldn't get state from browser");
  1486.   problem.displayer_.browserUnselected();
  1487. }
  1488.  
  1489. /**
  1490.  * Checks to see if the problem browser is selected, and if so, 
  1491.  * tell it it to show its warning.
  1492.  *
  1493.  * @param browser Reference to the browser we wish to check
  1494.  */
  1495. PROT_BrowserView.prototype.problemBrowserMaybeSelected = function(browser) {
  1496.   var problem = this.getCurrentProblem_(browser);
  1497.  
  1498.   if (this.tabWatcher_.getCurrentBrowser() === browser &&
  1499.       problem &&
  1500.       problem.displayer_.isActive()) 
  1501.     this.problemBrowserSelected(browser);
  1502. }
  1503.  
  1504. /**
  1505.  * Invoked by the controller when the user switches tabs to a problem tab
  1506.  *
  1507.  * @param browser Reference to the tab that was switched to
  1508.  */
  1509. PROT_BrowserView.prototype.problemBrowserSelected = function(browser) {
  1510.   G_Debug(this, "Problem browser selected");
  1511.   var problem = this.getCurrentProblem_(browser);
  1512.   G_Assert(this, !!problem, "No state? But we're selected!");
  1513.   problem.displayer_.browserSelected();
  1514. }
  1515.  
  1516. /**
  1517.  * Invoked by the controller when the user accepts our warning. Passes
  1518.  * the accept through to the message displayer, which knows what to do
  1519.  * (it will be displayer-specific).
  1520.  *
  1521.  * @param browser Reference to the browser for which the user accepted
  1522.  *                our warning
  1523.  */
  1524. PROT_BrowserView.prototype.acceptAction = function(browser) {
  1525.   var problem = this.getCurrentProblem_(browser);
  1526.  
  1527.   // We run the action only after we're completely through processing
  1528.   // this event. We do this because the action could cause state to be
  1529.   // cleared (e.g., by navigating the problem document) that we need
  1530.   // to finish processing the event.
  1531.  
  1532.   new G_Alarm(BindToObject(problem.displayer_.acceptAction, 
  1533.                            problem.displayer_), 
  1534.               0);
  1535. }
  1536.  
  1537. /**
  1538.  * Invoked by the controller when the user declines our
  1539.  * warning. Passes the decline through to the message displayer, which
  1540.  * knows what to do (it will be displayer-specific).
  1541.  *
  1542.  * @param browser Reference to the browser for which the user declined
  1543.  *                our warning
  1544.  */
  1545. PROT_BrowserView.prototype.declineAction = function(browser) {
  1546.   var problem = this.getCurrentProblem_(browser);
  1547.   G_Assert(this, !!problem, "User declined but no state???");
  1548.  
  1549.   // We run the action only after we're completely through processing
  1550.   // this event. We do this because the action could cause state to be
  1551.   // cleared (e.g., by navigating the problem document) that we need
  1552.   // to finish processing the event.
  1553.  
  1554.   new G_Alarm(BindToObject(problem.displayer_.declineAction, 
  1555.                            problem.displayer_), 
  1556.               0);
  1557. }
  1558.  
  1559. /**
  1560.  * The user wants to see the warning message. So let em! At some point when
  1561.  * we have multiple types of warnings, we'll have to mediate them here.
  1562.  *
  1563.  * @param browser Reference to the browser that has the warning the user 
  1564.  *                wants to see. 
  1565.  */
  1566. PROT_BrowserView.prototype.explicitShow = function(browser) {
  1567.   var problem = this.getCurrentProblem_(browser);
  1568.   G_Assert(this, !!problem, "Explicit show on browser w/o problem state???");
  1569.   problem.displayer_.explicitShow();
  1570. }
  1571. /* ***** BEGIN LICENSE BLOCK *****
  1572.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  1573.  *
  1574.  * The contents of this file are subject to the Mozilla Public License Version
  1575.  * 1.1 (the "License"); you may not use this file except in compliance with
  1576.  * the License. You may obtain a copy of the License at
  1577.  * http://www.mozilla.org/MPL/
  1578.  *
  1579.  * Software distributed under the License is distributed on an "AS IS" basis,
  1580.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  1581.  * for the specific language governing rights and limitations under the
  1582.  * License.
  1583.  *
  1584.  * The Original Code is Google Safe Browsing.
  1585.  *
  1586.  * The Initial Developer of the Original Code is Google Inc.
  1587.  * Portions created by the Initial Developer are Copyright (C) 2006
  1588.  * the Initial Developer. All Rights Reserved.
  1589.  *
  1590.  * Contributor(s):
  1591.  *   Fritz Schneider <fritz@google.com> (original author)
  1592.  *
  1593.  * Alternatively, the contents of this file may be used under the terms of
  1594.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  1595.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1596.  * in which case the provisions of the GPL or the LGPL are applicable instead
  1597.  * of those above. If you wish to allow use of your version of this file only
  1598.  * under the terms of either the GPL or the LGPL, and not to allow others to
  1599.  * use your version of this file under the terms of the MPL, indicate your
  1600.  * decision by deleting the provisions above and replace them with the notice
  1601.  * and other provisions required by the GPL or the LGPL. If you do not delete
  1602.  * the provisions above, a recipient may use your version of this file under
  1603.  * the terms of any one of the MPL, the GPL or the LGPL.
  1604.  *
  1605.  * ***** END LICENSE BLOCK ***** */
  1606.  
  1607.  
  1608. // This is our controller -- the thingy that listens to what the user
  1609. // is doing. There is one controller per browser window, and each has
  1610. // a BrowserView that manages information about problems within the
  1611. // window. The controller figures out when the browser might want to
  1612. // know about something, but the browser view figures out what exactly
  1613. // to do (and the BrowserView's displayer figures out how to do it).
  1614. //
  1615. // For example, the controller might notice that the user has switched
  1616. // to a tab that has something problematic in it. It would tell its 
  1617. // BrowserView this, and the BrowserView would figure out whether it 
  1618. // is appropriate to show a warning (e.g., perhaps the user previously
  1619. // dismissed the warning for that problem). If so, the BrowserView tells
  1620. // the displayer to show the warning. Anyhoo...
  1621. //
  1622. // TODO Could move all browser-related hide/show logic into the browser
  1623. //      view. Need to think about this more.
  1624.  
  1625. /**
  1626.  * Handles user actions, translating them into messages to the view
  1627.  *
  1628.  * @constructor
  1629.  * @param win Reference to the Window (browser window context) we should
  1630.  *            attach to
  1631.  * @param tabWatcher  Reference to the TabbedBrowserWatcher object 
  1632.  *                    the controller should use to receive events about tabs.
  1633.  * @param phishingWarden Reference to the PhishingWarden we should register
  1634.  *                       our browserview with
  1635.  */
  1636. function PROT_Controller(win, tabWatcher, phishingWarden) {
  1637.   this.debugZone = "controller";
  1638.  
  1639.   this.win_ = win;
  1640.   this.phishingWarden_ = phishingWarden;
  1641.  
  1642.   // Use this to query preferences
  1643.   this.prefs_ = new G_Preferences();
  1644.  
  1645.   // Set us up to receive the events we want.
  1646.   this.tabWatcher_ = tabWatcher;
  1647.   this.onTabSwitchCallback_ = BindToObject(this.onTabSwitch, this);
  1648.   this.tabWatcher_.registerListener("tabswitch",
  1649.                                     this.onTabSwitchCallback_);
  1650.  
  1651.  
  1652.   // Install our command controllers. These commands are issued from
  1653.   // various places in our UI, including our preferences dialog, the
  1654.   // warning dialog, etc.
  1655.   var commandHandlers = { 
  1656.     "safebrowsing-show-warning" :
  1657.       BindToObject(this.onUserShowWarning, this),
  1658.     "safebrowsing-accept-warning" :
  1659.       BindToObject(this.onUserAcceptWarning, this),
  1660.     "safebrowsing-decline-warning" :
  1661.       BindToObject(this.onUserDeclineWarning, this),
  1662.   };
  1663.  
  1664.   this.commandController_ = new PROT_CommandController(commandHandlers);
  1665.   this.win_.controllers.appendController(this.commandController_);
  1666.  
  1667.   // This guy embodies the logic of when to display warnings
  1668.   // (displayers embody the how).
  1669.   this.browserView_ = new PROT_BrowserView(this.tabWatcher_, 
  1670.                                            this.win_.document);
  1671.  
  1672.   // We need to let the phishing warden know about this browser view so it 
  1673.   // can be given the opportunity to handle problem documents. We also need
  1674.   // to let the warden know when this window and hence this browser view
  1675.   // is going away.
  1676.   this.phishingWarden_.addBrowserView(this.browserView_);
  1677.  
  1678.   this.windowWatcher_ = 
  1679.     Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  1680.     .getService(Components.interfaces.nsIWindowWatcher);
  1681.  
  1682.   G_Debug(this, "Controller initialized.");
  1683. }
  1684.  
  1685. /**
  1686.  * Invoked when the browser window is closing. Do some cleanup.
  1687.  */
  1688. PROT_Controller.prototype.shutdown = function(e) {
  1689.   G_Debug(this, "Browser window closing. Shutting controller down.");
  1690.   if (this.browserView_) {
  1691.     this.phishingWarden_.removeBrowserView(this.browserView_);
  1692.   }
  1693.  
  1694.   if (this.commandController_) {
  1695.     this.win_.controllers.removeController(this.commandController_);
  1696.     this.commandController_ = null;
  1697.   }
  1698.  
  1699.  
  1700.   // No need to drain the browser view's problem queue explicitly; it will
  1701.   // receive pagehides for all the browsers in its queues as they're torn
  1702.   // down, and it will remove them.
  1703.   this.browserView_ = null;
  1704.  
  1705.   if (this.tabWatcher_) {
  1706.     this.tabWatcher_.removeListener("tabswitch", 
  1707.                                     this.onTabSwitchCallback_);
  1708.     this.tabWatcher_.shutdown();
  1709.   }
  1710.  
  1711.   this.win_.removeEventListener("unload", this.onShutdown_, false);
  1712.   this.prefs_ = null;
  1713.  
  1714.   this.windowWatcher_ = null;
  1715.  
  1716.   G_Debug(this, "Controller shut down.");
  1717. }
  1718.  
  1719. /**
  1720.  * The user clicked the urlbar icon; they want to see the warning message
  1721.  * again.
  1722.  */
  1723. PROT_Controller.prototype.onUserShowWarning = function() {
  1724.   var browser = this.tabWatcher_.getCurrentBrowser();
  1725.   this.browserView_.explicitShow(browser);
  1726. }
  1727.  
  1728. /**
  1729.  * Deal with a user accepting our warning. 
  1730.  *
  1731.  * TODO the warning hide/display instructions here can probably be moved
  1732.  * into the browserview in the future, given its knowledge of when the
  1733.  * problem doc hides/shows.
  1734.  */
  1735. PROT_Controller.prototype.onUserAcceptWarning = function() {
  1736.   G_Debug(this, "User accepted warning.");
  1737.   var browser = this.tabWatcher_.getCurrentBrowser();
  1738.   G_Assert(this, !!browser, "Couldn't get current browser?!?");
  1739.   G_Assert(this, this.browserView_.hasProblem(browser),
  1740.            "User accept fired, but browser doesn't have warning showing?!?");
  1741.  
  1742.   this.browserView_.acceptAction(browser);
  1743.   this.browserView_.problemResolved(browser);
  1744. }
  1745.  
  1746. /**
  1747.  * Deal with a user declining our warning. 
  1748.  *
  1749.  * TODO the warning hide/display instructions here can probably be moved
  1750.  * into the browserview in the future, given its knowledge of when the
  1751.  * problem doc hides/shows.
  1752.  */
  1753. PROT_Controller.prototype.onUserDeclineWarning = function() {
  1754.   G_Debug(this, "User declined warning.");
  1755.   var browser = this.tabWatcher_.getCurrentBrowser();
  1756.   G_Assert(this, this.browserView_.hasProblem(browser),
  1757.            "User decline fired, but browser doesn't have warning showing?!?");
  1758.   this.browserView_.declineAction(browser);
  1759.   // We don't call problemResolved() here because all declining does it
  1760.   // hide the message; we still have the urlbar icon showing, giving
  1761.   // the user the ability to bring the warning message back up if they
  1762.   // so desire.
  1763. }
  1764.  
  1765. /**
  1766.  * Notice tab switches, and display or hide warnings as appropriate.
  1767.  *
  1768.  * TODO this logic can probably move into the browser view at some
  1769.  * point. But one thing at a time.
  1770.  */
  1771. PROT_Controller.prototype.onTabSwitch = function(e) {
  1772.   if (this.browserView_.hasProblem(e.fromBrowser)) 
  1773.     this.browserView_.problemBrowserUnselected(e.fromBrowser);
  1774.  
  1775.   if (this.browserView_.hasProblem(e.toBrowser))
  1776.     this.browserView_.problemBrowserSelected(e.toBrowser);
  1777. }
  1778. /* ***** BEGIN LICENSE BLOCK *****
  1779.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  1780.  *
  1781.  * The contents of this file are subject to the Mozilla Public License Version
  1782.  * 1.1 (the "License"); you may not use this file except in compliance with
  1783.  * the License. You may obtain a copy of the License at
  1784.  * http://www.mozilla.org/MPL/
  1785.  *
  1786.  * Software distributed under the License is distributed on an "AS IS" basis,
  1787.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  1788.  * for the specific language governing rights and limitations under the
  1789.  * License.
  1790.  *
  1791.  * The Original Code is Google Safe Browsing.
  1792.  *
  1793.  * The Initial Developer of the Original Code is Google Inc.
  1794.  * Portions created by the Initial Developer are Copyright (C) 2006
  1795.  * the Initial Developer. All Rights Reserved.
  1796.  *
  1797.  * Contributor(s):
  1798.  *   Fritz Schneider <fritz@google.com> (original author)
  1799.  *
  1800.  * Alternatively, the contents of this file may be used under the terms of
  1801.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  1802.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1803.  * in which case the provisions of the GPL or the LGPL are applicable instead
  1804.  * of those above. If you wish to allow use of your version of this file only
  1805.  * under the terms of either the GPL or the LGPL, and not to allow others to
  1806.  * use your version of this file under the terms of the MPL, indicate your
  1807.  * decision by deleting the provisions above and replace them with the notice
  1808.  * and other provisions required by the GPL or the LGPL. If you do not delete
  1809.  * the provisions above, a recipient may use your version of this file under
  1810.  * the terms of any one of the MPL, the GPL or the LGPL.
  1811.  *
  1812.  * ***** END LICENSE BLOCK ***** */
  1813.  
  1814.  
  1815. // Some misc command-related plumbing used by the controller.
  1816.  
  1817.  
  1818. /**
  1819.  * A tiny wrapper class for super-simple command handlers.
  1820.  *
  1821.  * @param commandHandlerMap An object containing name/value pairs where
  1822.  *                          the name is command name (string) and value
  1823.  *                          is the function to execute for that command
  1824.  */
  1825. function PROT_CommandController(commandHandlerMap) {
  1826.   this.debugZone = "commandhandler";
  1827.   this.cmds_ = commandHandlerMap;
  1828. }
  1829.  
  1830. /**
  1831.  * @param cmd Command to query support for
  1832.  * @returns Boolean indicating if this controller supports cmd
  1833.  */
  1834. PROT_CommandController.prototype.supportsCommand = function(cmd) { 
  1835.   return (cmd in this.cmds_); 
  1836. }
  1837.  
  1838. /**
  1839.  * Trivial implementation
  1840.  *
  1841.  * @param cmd Command to query status of
  1842.  */
  1843. PROT_CommandController.prototype.isCommandEnabled = function(cmd) { 
  1844.   return true; 
  1845. }
  1846.   
  1847. /**
  1848.  * Execute a command
  1849.  *
  1850.  * @param cmd Command to execute
  1851.  */
  1852. PROT_CommandController.prototype.doCommand = function(cmd) {
  1853.   return this.cmds_[cmd](); 
  1854. }
  1855.  
  1856. /**
  1857.  * Trivial implementation
  1858.  */
  1859. PROT_CommandController.prototype.onEvent = function(cmd) { }
  1860.  
  1861. /* ***** BEGIN LICENSE BLOCK *****
  1862.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  1863.  *
  1864.  * The contents of this file are subject to the Mozilla Public License Version
  1865.  * 1.1 (the "License"); you may not use this file except in compliance with
  1866.  * the License. You may obtain a copy of the License at
  1867.  * http://www.mozilla.org/MPL/
  1868.  *
  1869.  * Software distributed under the License is distributed on an "AS IS" basis,
  1870.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  1871.  * for the specific language governing rights and limitations under the
  1872.  * License.
  1873.  *
  1874.  * The Original Code is Google Safe Browsing.
  1875.  *
  1876.  * The Initial Developer of the Original Code is Google Inc.
  1877.  * Portions created by the Initial Developer are Copyright (C) 2006
  1878.  * the Initial Developer. All Rights Reserved.
  1879.  *
  1880.  * Contributor(s):
  1881.  *   Fritz Schneider <fritz@google.com> (original author)
  1882.  *   J. Paul Reed <preed@mozilla.com>
  1883.  *
  1884.  * Alternatively, the contents of this file may be used under the terms of
  1885.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  1886.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1887.  * in which case the provisions of the GPL or the LGPL are applicable instead
  1888.  * of those above. If you wish to allow use of your version of this file only
  1889.  * under the terms of either the GPL or the LGPL, and not to allow others to
  1890.  * use your version of this file under the terms of the MPL, indicate your
  1891.  * decision by deleting the provisions above and replace them with the notice
  1892.  * and other provisions required by the GPL or the LGPL. If you do not delete
  1893.  * the provisions above, a recipient may use your version of this file under
  1894.  * the terms of any one of the MPL, the GPL or the LGPL.
  1895.  *
  1896.  * ***** END LICENSE BLOCK ***** */
  1897.  
  1898.  
  1899. // A class that encapsulates data provider specific values.  The
  1900. // root of the provider pref tree is browser.safebrowsing.provider.
  1901. // followed by a number, followed by specific properties.  The properties
  1902. // that a data provider can supply are:
  1903. //
  1904. // name: The name of the provider
  1905. // lookupURL: The URL to send requests to in enhanced mode
  1906. // keyURL: Before we send URLs in enhanced mode, we need to encrypt them
  1907. // reportURL: When shown a warning bubble, we send back the user decision
  1908. //            (get me out of here/ignore warning) to this URL (strip cookies
  1909. //            first).  This is optional.
  1910. // reportGenericURL: HTML page for general user feedback
  1911. // reportPhishURL: HTML page for notifying the provider of a new phishing page
  1912. // reportErrorURL: HTML page for notifying the provider of a false positive
  1913.  
  1914. const kDataProviderIdPref = 'browser.safebrowsing.dataProvider';
  1915. const kProviderBasePref = 'browser.safebrowsing.provider.';
  1916.  
  1917. //@line 60 "/cygdrive/K/tinderbuild/src/flock/mozilla/browser/components/safebrowsing/src/../content/globalstore.js"
  1918. const MOZ_OFFICIAL_BUILD = false;
  1919. //@line 62 "/cygdrive/K/tinderbuild/src/flock/mozilla/browser/components/safebrowsing/src/../content/globalstore.js"
  1920.  
  1921. const MOZ_PARAM_LOCALE = /\{moz:locale\}/g;
  1922. const MOZ_PARAM_CLIENT = /\{moz:client\}/g;
  1923. const MOZ_PARAM_BUILDID = /\{moz:buildid\}/g;
  1924. const MOZ_PARAM_VERSION = /\{moz:version\}/g;
  1925.  
  1926. /**
  1927.  * Information regarding the data provider.
  1928.  */
  1929. function PROT_DataProvider() {
  1930.   this.prefs_ = new G_Preferences();
  1931.  
  1932.   this.loadDataProviderPrefs_();
  1933.   
  1934.   // Watch for changes in the data provider and update accordingly.
  1935.   this.prefs_.addObserver(kDataProviderIdPref,
  1936.                           BindToObject(this.loadDataProviderPrefs_, this));
  1937.  
  1938.   // Watch for when anti-phishing is toggled on or off.
  1939.   this.prefs_.addObserver(kPhishWardenEnabledPref,
  1940.                           BindToObject(this.loadDataProviderPrefs_, this));
  1941.  
  1942.   // Watch for when remote lookups are toggled on or off.
  1943.   this.prefs_.addObserver(kPhishWardenRemoteLookups,
  1944.                           BindToObject(this.loadDataProviderPrefs_, this));
  1945. }
  1946.  
  1947. /**
  1948.  * Populate all the provider variables.  We also call this when whenever
  1949.  * the provider id changes.
  1950.  */
  1951. PROT_DataProvider.prototype.loadDataProviderPrefs_ = function() {
  1952.   // Currently, there's no UI for changing local list provider so we
  1953.   // hard code the value for provider 0.
  1954.   this.updateURL_ = this.getUrlPref_(
  1955.         'browser.safebrowsing.provider.0.updateURL');
  1956.  
  1957.   var id = this.prefs_.getPref(kDataProviderIdPref, null);
  1958.  
  1959.   // default to 0
  1960.   if (null == id)
  1961.     id = 0;
  1962.   
  1963.   var basePref = kProviderBasePref + id + '.';
  1964.  
  1965.   this.name_ = this.prefs_.getPref(basePref + "name", "");
  1966.  
  1967.   // Urls used to get data from a provider
  1968.   this.lookupURL_ = this.getUrlPref_(basePref + "lookupURL");
  1969.   this.keyURL_ = this.getUrlPref_(basePref + "keyURL");
  1970.   this.reportURL_ = this.getUrlPref_(basePref + "reportURL");
  1971.  
  1972.   // Urls to HTML report pages
  1973.   this.reportGenericURL_ = this.getUrlPref_(basePref + "reportGenericURL");
  1974.   this.reportErrorURL_ = this.getUrlPref_(basePref + "reportErrorURL");
  1975.   this.reportPhishURL_ = this.getUrlPref_(basePref + "reportPhishURL");
  1976.  
  1977.   // Propogate the changes to the list-manager.
  1978.   this.updateListManager_();
  1979. }
  1980.  
  1981. /**
  1982.  * The list manager needs urls to operate.  It needs a url to know where the
  1983.  * table updates are, and it needs a url for decrypting enchash style tables.
  1984.  */
  1985. PROT_DataProvider.prototype.updateListManager_ = function() {
  1986.   var listManager = Cc["@mozilla.org/url-classifier/listmanager;1"]
  1987.                       .getService(Ci.nsIUrlListManager);
  1988.  
  1989.   // If we add support for changing local data providers, we need to add a
  1990.   // pref observer that sets the update url accordingly.
  1991.   listManager.setUpdateUrl(this.getUpdateURL());
  1992.  
  1993.   // setKeyUrl has the side effect of fetching a key from the server.
  1994.   // This shouldn't happen if anti-phishing is disabled or we're in local
  1995.   // list mode, so we need to check for that.
  1996.   var isEnabled = this.prefs_.getPref(kPhishWardenEnabledPref, false);
  1997.   var remoteLookups = this.prefs_.getPref(kPhishWardenRemoteLookups, false);
  1998.   if (isEnabled && remoteLookups) {
  1999.     listManager.setKeyUrl(this.getKeyURL());
  2000.   } else {
  2001.     // Clear the key to stop updates.
  2002.     listManager.setKeyUrl("");
  2003.   }
  2004. }
  2005.  
  2006. /**
  2007.  * Lookup the value of a URL from prefs file and do parameter substitution.
  2008.  */
  2009. PROT_DataProvider.prototype.getUrlPref_ = function(prefName) {
  2010.   var url = this.prefs_.getPref(prefName);
  2011.  
  2012.   var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
  2013.                           .getService(Components.interfaces.nsIXULAppInfo);
  2014.  
  2015.   var mozClientStr = MOZ_OFFICIAL_BUILD ? 'navclient-auto-ffox' : appInfo.name;
  2016.  
  2017.   // Parameter substitution
  2018.   url = url.replace(MOZ_PARAM_LOCALE, this.getLocale_());
  2019.   url = url.replace(MOZ_PARAM_CLIENT, mozClientStr);
  2020.   url = url.replace(MOZ_PARAM_BUILDID, appInfo.appBuildID);
  2021.   url = url.replace(MOZ_PARAM_VERSION, appInfo.version);
  2022.   return url;
  2023. }
  2024.  
  2025. /**
  2026.  * @return String the browser locale (similar code is in nsSearchService.js)
  2027.  */
  2028. PROT_DataProvider.prototype.getLocale_ = function() {
  2029.   const localePref = "general.useragent.locale";
  2030.   var locale = this.getLocalizedPref_(localePref);
  2031.   if (locale)
  2032.     return locale;
  2033.  
  2034.   // Not localized
  2035.   var prefs = new G_Preferences();
  2036.   return prefs.getPref(localePref, "");
  2037. }
  2038.  
  2039. /**
  2040.  * @return String name of the localized pref, null if none exists.
  2041.  */
  2042. PROT_DataProvider.prototype.getLocalizedPref_ = function(aPrefName) {
  2043.   // G_Preferences doesn't know about complex values, so we use the
  2044.   // xpcom object directly.
  2045.   var prefs = Cc["@mozilla.org/preferences-service;1"]
  2046.               .getService(Ci.nsIPrefBranch);
  2047.   try {
  2048.     return prefs.getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data;
  2049.   } catch (ex) {
  2050.   }
  2051.   return "";
  2052. }
  2053.  
  2054. //////////////////////////////////////////////////////////////////////////////
  2055. // Getters for the remote provider pref values mentioned above.
  2056. PROT_DataProvider.prototype.getName = function() {
  2057.   return this.name_;
  2058. }
  2059.  
  2060. PROT_DataProvider.prototype.getUpdateURL = function() {
  2061.   return this.updateURL_;
  2062. }
  2063.  
  2064. PROT_DataProvider.prototype.getLookupURL = function() {
  2065.   return this.lookupURL_;
  2066. }
  2067. PROT_DataProvider.prototype.getKeyURL = function() {
  2068.   return this.keyURL_;
  2069. }
  2070. PROT_DataProvider.prototype.getReportURL = function() {
  2071.   return this.reportURL_;
  2072. }
  2073.  
  2074. PROT_DataProvider.prototype.getReportGenericURL = function() {
  2075.   return this.reportGenericURL_;
  2076. }
  2077. PROT_DataProvider.prototype.getReportErrorURL = function() {
  2078.   return this.reportErrorURL_;
  2079. }
  2080. PROT_DataProvider.prototype.getReportPhishURL = function() {
  2081.   return this.reportPhishURL_;
  2082. }
  2083. /* ***** BEGIN LICENSE BLOCK *****
  2084.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2085.  *
  2086.  * The contents of this file are subject to the Mozilla Public License Version
  2087.  * 1.1 (the "License"); you may not use this file except in compliance with
  2088.  * the License. You may obtain a copy of the License at
  2089.  * http://www.mozilla.org/MPL/
  2090.  *
  2091.  * Software distributed under the License is distributed on an "AS IS" basis,
  2092.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2093.  * for the specific language governing rights and limitations under the
  2094.  * License.
  2095.  *
  2096.  * The Original Code is Google Safe Browsing.
  2097.  *
  2098.  * The Initial Developer of the Original Code is Google Inc.
  2099.  * Portions created by the Initial Developer are Copyright (C) 2006
  2100.  * the Initial Developer. All Rights Reserved.
  2101.  *
  2102.  * Contributor(s):
  2103.  *   Niels Provos <niels@google.com> (original author)d
  2104.  
  2105.  *   Fritz Schneider <fritz@google.com>
  2106.  *
  2107.  * Alternatively, the contents of this file may be used under the terms of
  2108.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2109.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2110.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2111.  * of those above. If you wish to allow use of your version of this file only
  2112.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2113.  * use your version of this file under the terms of the MPL, indicate your
  2114.  * decision by deleting the provisions above and replace them with the notice
  2115.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2116.  * the provisions above, a recipient may use your version of this file under
  2117.  * the terms of any one of the MPL, the GPL or the LGPL.
  2118.  *
  2119.  * ***** END LICENSE BLOCK ***** */
  2120.  
  2121. // A warden that knows how to register lists with a listmanager and keep them
  2122. // updated if necessary.  The ListWarden also provides a simple interface to
  2123. // check if a URL is evil or not.  Specialized wardens like the PhishingWarden
  2124. // inherit from it.
  2125. //
  2126. // Classes that inherit from ListWarden are responsible for calling
  2127. // enableTableUpdates or disableTableUpdates.  This usually entails
  2128. // registering prefObservers and calling enable or disable in the base
  2129. // class as appropriate.
  2130. //
  2131.  
  2132. /**
  2133.  * Abtracts the checking of user/browser actions for signs of
  2134.  * phishing. 
  2135.  *
  2136.  * @constructor
  2137.  */
  2138. function PROT_ListWarden() {
  2139.   this.debugZone = "listwarden";
  2140.   var listManager = Cc["@mozilla.org/url-classifier/listmanager;1"]
  2141.                       .getService(Ci.nsIUrlListManager);
  2142.   this.listManager_ = listManager;
  2143.  
  2144.   // Once we register tables, their respective names will be listed here.
  2145.   this.blackTables_ = [];
  2146.   this.whiteTables_ = [];
  2147. }
  2148.  
  2149. PROT_ListWarden.IN_BLACKLIST = 0
  2150. PROT_ListWarden.IN_WHITELIST = 1
  2151. PROT_ListWarden.NOT_FOUND = 2
  2152.  
  2153. /**
  2154.  * Tell the ListManger to keep all of our tables updated
  2155.  */
  2156.  
  2157. PROT_ListWarden.prototype.enableBlacklistTableUpdates = function() {
  2158.   for (var i = 0; i < this.blackTables_.length; ++i) {
  2159.     this.listManager_.enableUpdate(this.blackTables_[i]);
  2160.   }
  2161. }
  2162.  
  2163. /**
  2164.  * Tell the ListManager to stop updating our tables
  2165.  */
  2166.  
  2167. PROT_ListWarden.prototype.disableBlacklistTableUpdates = function() {
  2168.   for (var i = 0; i < this.blackTables_.length; ++i) {
  2169.     this.listManager_.disableUpdate(this.blackTables_[i]);
  2170.   }
  2171. }
  2172.  
  2173. /**
  2174.  * Tell the ListManager to update whitelist tables.  They may be enabled even
  2175.  * when other updates aren't, for performance reasons.
  2176.  */
  2177. PROT_ListWarden.prototype.enableWhitelistTableUpdates = function() {
  2178.   for (var i = 0; i < this.whiteTables_.length; ++i) {
  2179.     this.listManager_.enableUpdate(this.whiteTables_[i]);
  2180.   }
  2181. }
  2182.  
  2183. /**
  2184.  * Tell the ListManager to stop updating whitelist tables.
  2185.  */
  2186. PROT_ListWarden.prototype.disableWhitelistTableUpdates = function() {
  2187.   for (var i = 0; i < this.whiteTables_.length; ++i) {
  2188.     this.listManager_.disableUpdate(this.whiteTables_[i]);
  2189.   }
  2190. }
  2191.  
  2192. /**
  2193.  * Register a new black list table with the list manager
  2194.  * @param tableName - name of the table to register
  2195.  * @returns true if the table could be registered, false otherwise
  2196.  */
  2197.  
  2198. PROT_ListWarden.prototype.registerBlackTable = function(tableName) {
  2199.   var result = this.listManager_.registerTable(tableName, false);
  2200.   if (result) {
  2201.     this.blackTables_.push(tableName);
  2202.   }
  2203.   return result;
  2204. }
  2205.  
  2206. /**
  2207.  * Register a new white list table with the list manager
  2208.  * @param tableName - name of the table to register
  2209.  * @returns true if the table could be registered, false otherwise
  2210.  */
  2211.  
  2212. PROT_ListWarden.prototype.registerWhiteTable = function(tableName) {
  2213.   var result = this.listManager_.registerTable(tableName, false);
  2214.   if (result) {
  2215.     this.whiteTables_.push(tableName);
  2216.   }
  2217.   return result;
  2218. }
  2219.  
  2220. /**
  2221.  * Method that looks up a url on the whitelist.
  2222.  *
  2223.  * @param url The URL to check
  2224.  * @param callback Function with a single param:
  2225.  *       PROT_ListWarden.IN_BLACKLIST, PROT_ListWarden.IN_WHITELIST,
  2226.  *       or PROT_ListWarden.NOT_FOUND
  2227.  */
  2228. PROT_ListWarden.prototype.isWhiteURL = function(url, callback) {
  2229.   (new MultiTableQuerier(url,
  2230.                          this.whiteTables_,
  2231.                          [] /* no blacklists */,
  2232.                          callback)).run();
  2233. }
  2234.  
  2235. /**
  2236.  * Method that looks up a url in both the white and black lists.
  2237.  *
  2238.  * If there is conflict, the white list has precedence over the black list.
  2239.  *
  2240.  * This is tricky because all database queries are asynchronous.  So we need
  2241.  * to chain together our checks against white and black tables.  We use
  2242.  * MultiTableQuerier (see below) to manage this.
  2243.  *
  2244.  * @param url URL to look up
  2245.  * @param callback Function with a single param:
  2246.  *       PROT_ListWarden.IN_BLACKLIST, PROT_ListWarden.IN_WHITELIST,
  2247.  *       or PROT_ListWarden.NOT_FOUND
  2248.  */
  2249. PROT_ListWarden.prototype.isEvilURL = function(url, callback) {
  2250.   (new MultiTableQuerier(url,
  2251.                          this.whiteTables_,
  2252.                          this.blackTables_,
  2253.                          callback)).run();
  2254. }
  2255.  
  2256. /**
  2257.  * This class helps us query multiple tables even though each table check
  2258.  * is asynchronous.  It provides callbacks for each listManager lookup
  2259.  * and decides whether we need to continue querying or not.  After
  2260.  * instantiating the method, use run() to invoke.
  2261.  *
  2262.  * @param url String The url to check
  2263.  * @param whiteTables Array of strings with each white table name
  2264.  * @param blackTables Array of strings with each black table name
  2265.  * @param callback Function to call with result 
  2266.  *       PROT_ListWarden.IN_BLACKLIST, PROT_ListWarden.IN_WHITELIST,
  2267.  *       or PROT_ListWarden.NOT_FOUND
  2268.  */
  2269. function MultiTableQuerier(url, whiteTables, blackTables, callback) {
  2270.   this.debugZone = "multitablequerier";
  2271.   this.url_ = url;
  2272.  
  2273.   this.whiteTables_ = whiteTables;
  2274.   this.blackTables_ = blackTables;
  2275.   this.whiteIdx_ = 0;
  2276.   this.blackIdx_ = 0;
  2277.  
  2278.   this.callback_ = callback;
  2279.   this.listManager_ = Cc["@mozilla.org/url-classifier/listmanager;1"]
  2280.                       .getService(Ci.nsIUrlListManager);
  2281. }
  2282.  
  2283. /**
  2284.  * We first query the white tables in succession.  If any contain
  2285.  * the url, we stop.  If none contain the url, we query the black tables
  2286.  * in succession.  If any contain the url, we call callback and
  2287.  * stop.  If none of the black tables contain the url, then we just stop
  2288.  * (i.e., it's not black url).
  2289.  */
  2290. MultiTableQuerier.prototype.run = function() {
  2291.   var whiteTable = this.whiteTables_[this.whiteIdx_];
  2292.   var blackTable = this.blackTables_[this.blackIdx_];
  2293.   if (whiteTable) {
  2294.     //G_Debug(this, "Looking in whitetable: " + whiteTable);
  2295.     ++this.whiteIdx_;
  2296.     this.listManager_.safeExists(whiteTable, this.url_,
  2297.                                  BindToObject(this.whiteTableCallback_,
  2298.                                               this));
  2299.   } else if (blackTable) {
  2300.     //G_Debug(this, "Looking in blacktable: " + blackTable);
  2301.     ++this.blackIdx_;
  2302.     this.listManager_.safeExists(blackTable, this.url_,
  2303.                                  BindToObject(this.blackTableCallback_,
  2304.                                               this));
  2305.   } else {
  2306.     // No tables left to check, so we quit.
  2307.     G_Debug(this, "Not found in any tables: " + this.url_);
  2308.     this.callback_(PROT_ListWarden.NOT_FOUND);
  2309.  
  2310.     // Break circular ref to callback.
  2311.     this.callback_ = null;
  2312.     this.listManager_ = null;
  2313.   }
  2314. }
  2315.  
  2316. /**
  2317.  * After checking a white table, we return here.  If the url is found,
  2318.  * we can stop.  Otherwise, we call run again.
  2319.  */
  2320. MultiTableQuerier.prototype.whiteTableCallback_ = function(isFound) {
  2321.   //G_Debug(this, "whiteTableCallback_: " + isFound);
  2322.   if (!isFound)
  2323.     this.run();
  2324.   else {
  2325.     G_Debug(this, "Found in whitelist: " + this.url_)
  2326.     this.callback_(PROT_ListWarden.IN_WHITELIST);
  2327.  
  2328.     // Break circular ref to callback.
  2329.     this.callback_ = null;
  2330.     this.listManager_ = null;
  2331.   }
  2332. }
  2333.  
  2334. /**
  2335.  * After checking a black table, we return here.  If the url is found,
  2336.  * we can call the callback and stop.  Otherwise, we call run again.
  2337.  */
  2338. MultiTableQuerier.prototype.blackTableCallback_ = function(isFound) {
  2339.   //G_Debug(this, "blackTableCallback_: " + isFound);
  2340.   if (!isFound) {
  2341.     this.run();
  2342.   } else {
  2343.     // In the blacklist, must be an evil url.
  2344.     G_Debug(this, "Found in blacklist: " + this.url_)
  2345.     this.callback_(PROT_ListWarden.IN_BLACKLIST);
  2346.  
  2347.     // Break circular ref to callback.
  2348.     this.callback_ = null;
  2349.     this.listManager_ = null;
  2350.   }
  2351. }
  2352. /* ***** BEGIN LICENSE BLOCK *****
  2353.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  2354.  *
  2355.  * The contents of this file are subject to the Mozilla Public License Version
  2356.  * 1.1 (the "License"); you may not use this file except in compliance with
  2357.  * the License. You may obtain a copy of the License at
  2358.  * http://www.mozilla.org/MPL/
  2359.  *
  2360.  * Software distributed under the License is distributed on an "AS IS" basis,
  2361.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2362.  * for the specific language governing rights and limitations under the
  2363.  * License.
  2364.  *
  2365.  * The Original Code is Google Safe Browsing.
  2366.  *
  2367.  * The Initial Developer of the Original Code is Google Inc.
  2368.  * Portions created by the Initial Developer are Copyright (C) 2006
  2369.  * the Initial Developer. All Rights Reserved.
  2370.  *
  2371.  * Contributor(s):
  2372.  *   Fritz Schneider <fritz@google.com> (original author)
  2373.  *
  2374.  * Alternatively, the contents of this file may be used under the terms of
  2375.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  2376.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  2377.  * in which case the provisions of the GPL or the LGPL are applicable instead
  2378.  * of those above. If you wish to allow use of your version of this file only
  2379.  * under the terms of either the GPL or the LGPL, and not to allow others to
  2380.  * use your version of this file under the terms of the MPL, indicate your
  2381.  * decision by deleting the provisions above and replace them with the notice
  2382.  * and other provisions required by the GPL or the LGPL. If you do not delete
  2383.  * the provisions above, a recipient may use your version of this file under
  2384.  * the terms of any one of the MPL, the GPL or the LGPL.
  2385.  *
  2386.  * ***** END LICENSE BLOCK ***** */
  2387.  
  2388.  
  2389. // Implementation of the warning message we show users when we
  2390. // notice navigation to a phishing page after it has loaded. The
  2391. // browser view encapsulates all the hide/show logic, so the displayer
  2392. // doesn't need to know when to display itself, only how.
  2393. //
  2394. // Displayers implement the following interface:
  2395. //
  2396. // start() -- fired to initialize the displayer (to make it active). When
  2397. //            called, this displayer starts listening for and responding to
  2398. //            events. At most one displayer per tab should be active at a 
  2399. //            time, and start() should be called at most once.
  2400. // declineAction() -- fired when the user declines the warning.
  2401. // acceptAction() -- fired when the user declines the warning
  2402. // explicitShow() -- fired when the user wants to see the warning again
  2403. // browserSelected() -- the browser is the top tab
  2404. // browserUnselected() -- the browser is no long the top tab
  2405. // done() -- clean up. May be called once (even if the displayer wasn't
  2406. //           activated).
  2407. //
  2408. // At the moment, all displayers share access to the same xul in 
  2409. // safebrowsing-overlay.xul. Hence the need for at most one displayer
  2410. // to be active per tab at a time.
  2411.  
  2412. /**
  2413.  * Factory that knows how to create a displayer appropriate to the
  2414.  * user's platform. We use a clunky canvas-based displayer for all
  2415.  * platforms until such time as we can overlay semi-transparent
  2416.  * areas over browser content.
  2417.  *
  2418.  * See the base object for a description of the constructor args
  2419.  *
  2420.  * @constructor
  2421.  */
  2422. function PROT_PhishMsgDisplayer(msgDesc, browser, doc, url) {
  2423.  
  2424.   // TODO: Change this to return a PhishMsgDisplayerTransp on windows
  2425.   // (and maybe other platforms) when Firefox 2.0 hits.
  2426.  
  2427.   return new PROT_PhishMsgDisplayerCanvas(msgDesc, browser, doc, url);
  2428. }
  2429.  
  2430.  
  2431. /**
  2432.  * Base class that implements most of the plumbing required to hide
  2433.  * and show a phishing warning. Subclasses implement the actual
  2434.  * showMessage and hideMessage methods. 
  2435.  *
  2436.  * This class is not meant to be instantiated directly.
  2437.  *
  2438.  * @param msgDesc String describing the kind of warning this is
  2439.  * @param browser Reference to the browser over which we display the msg
  2440.  * @param doc Reference to the document in which browser is found
  2441.  * @param url String containing url of the problem document
  2442.  * @constructor
  2443.  */
  2444. function PROT_PhishMsgDisplayerBase(msgDesc, browser, doc, url) {
  2445.   this.debugZone = "phishdisplayer";
  2446.   this.msgDesc_ = msgDesc;                                // currently unused
  2447.   this.browser_ = browser;
  2448.   this.doc_ = doc;
  2449.   this.url_ = url;
  2450.  
  2451.   // We'll need to manipulate the XUL in safebrowsing-overlay.xul
  2452.   this.messageId_ = "safebrowsing-palm-message";
  2453.   this.messageTailId_ = "safebrowsing-palm-message-tail-container";
  2454.   this.messageContentId_ = "safebrowsing-palm-message-content";
  2455.   this.extendedMessageId_ = "safebrowsing-palm-extended-message";
  2456.   this.showmoreLinkId_ = "safebrowsing-palm-showmore-link";
  2457.   this.faqLinkId_ = "safebrowsing-palm-faq-link";
  2458.   this.urlbarIconId_ = "safebrowsing-urlbar-icon";
  2459.   this.refElementId_ = this.urlbarIconId_;
  2460.  
  2461.   // We use this to report user actions to the server
  2462.   this.reporter_ = new PROT_Reporter();
  2463.  
  2464.   // The active UI elements in our warning send these commands; bind them
  2465.   // to their handlers but don't register the commands until we start
  2466.   // (because another displayer might be active)
  2467.   this.commandHandlers_ = {
  2468.     "safebrowsing-palm-showmore":
  2469.       BindToObject(this.showMore_, this),
  2470.   };
  2471.  
  2472.   this.windowWatcher_ = 
  2473.     Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  2474.     .getService(Components.interfaces.nsIWindowWatcher);
  2475. }
  2476.  
  2477. /**
  2478.  * @returns The default background color of the browser
  2479.  */
  2480. PROT_PhishMsgDisplayerBase.prototype.getBackgroundColor_ = function() {
  2481.   var pref = Components.classes["@mozilla.org/preferences-service;1"].
  2482.              getService(Components.interfaces.nsIPrefBranch);
  2483.   return pref.getCharPref("browser.display.background_color");
  2484. }
  2485.  
  2486. /**
  2487.  * Fired when the user declines our warning. Report it!
  2488.  */
  2489. PROT_PhishMsgDisplayerBase.prototype.declineAction = function() {
  2490.   G_Debug(this, "User declined warning.");
  2491.   G_Assert(this, this.started_, "Decline on a non-active displayer?");
  2492.   this.reporter_.report("phishdecline", this.url_);
  2493.  
  2494.   this.messageShouldShow_ = false;
  2495.   if (this.messageShowing_)
  2496.     this.hideMessage_();
  2497. }
  2498.  
  2499. /**
  2500.  * Fired when the user accepts our warning
  2501.  */
  2502. PROT_PhishMsgDisplayerBase.prototype.acceptAction = function() {
  2503.   G_Assert(this, this.started_, "Accept on an unstarted displayer?");
  2504.   G_Assert(this, this.done_, "Accept on a finished displayer?");
  2505.   G_Debug(this, "User accepted warning.");
  2506.   this.reporter_.report("phishaccept", this.url_);
  2507.  
  2508.   var url = this.getMeOutOfHereUrl_();
  2509.   this.browser_.loadURI(url);
  2510. }
  2511.  
  2512. /**
  2513.  * Get the url for "Get me out of here."  This is the browser's default home
  2514.  * page, or, about:blank.
  2515.  * @return String url
  2516.  */
  2517. PROT_PhishMsgDisplayerBase.prototype.getMeOutOfHereUrl_ = function() {
  2518.   // Try to get their homepage from prefs.
  2519.   var prefs = Cc["@mozilla.org/preferences-service;1"]
  2520.               .getService(Ci.nsIPrefService).getDefaultBranch(null);
  2521.  
  2522.   var url = "about:blank";
  2523.   try {
  2524.     url = prefs.getComplexValue("browser.startup.homepage",
  2525.                                 Ci.nsIPrefLocalizedString).data;
  2526.     // If url is a pipe-delimited set of pages, just take the first one.
  2527.     // This will need to change once bug 221445 is fixed.
  2528.     if (url.indexOf("|") != -1)
  2529.       url = url.split("|")[0];
  2530.   } catch(e) {
  2531.     G_Debug(this, "Couldn't get homepage pref: " + e);
  2532.   }
  2533.   
  2534.   return url;
  2535. }
  2536.  
  2537. /**
  2538.  * Invoked when the browser is resized
  2539.  */
  2540. PROT_PhishMsgDisplayerBase.prototype.onBrowserResized_ = function(event) {
  2541.   G_Debug(this, "Got resize for " + event.target);
  2542.  
  2543.   if (this.messageShowing_) {
  2544.     this.hideMessage_(); 
  2545.     this.showMessage_();
  2546.   }
  2547. }
  2548.  
  2549. /**
  2550.  * Invoked by the browser view when our browser is switched to
  2551.  */
  2552. PROT_PhishMsgDisplayerBase.prototype.browserSelected = function() {
  2553.   G_Assert(this, this.started_, "Displayer selected before being started???");
  2554.  
  2555.   // If messageshowing hasn't been set, then this is the first time this
  2556.   // problematic browser tab has been on top, so do our setup and show
  2557.   // the warning.
  2558.   if (this.messageShowing_ === undefined) {
  2559.     this.messageShouldShow_ = true;
  2560.   }
  2561.  
  2562.   this.hideLockIcon_();        // Comes back when we are unselected or unloaded
  2563.   this.addWarningInUrlbar_();  // Goes away when we are unselected or unloaded
  2564.  
  2565.   // messageShouldShow might be false if the user dismissed the warning, 
  2566.   // switched tabs, and then switched back. We're still active, but don't
  2567.   // want to show the warning again. The user can cause it to show by
  2568.   // clicking our icon in the urlbar.
  2569.   if (this.messageShouldShow_)
  2570.     this.showMessage_();
  2571. }
  2572.  
  2573. /**
  2574.  * Invoked to display the warning message explicitly, for example if the user
  2575.  * clicked the url warning icon.
  2576.  */
  2577. PROT_PhishMsgDisplayerBase.prototype.explicitShow = function() {
  2578.   this.messageShouldShow_ = true;
  2579.   if (!this.messageShowing_)
  2580.     this.showMessage_();
  2581. }
  2582.  
  2583. /** 
  2584.  * Invoked by the browser view when our browser is switched away from
  2585.  */
  2586. PROT_PhishMsgDisplayerBase.prototype.browserUnselected = function() {
  2587.   this.removeWarningInUrlbar_();
  2588.   this.unhideLockIcon_();
  2589.   if (this.messageShowing_)
  2590.     this.hideMessage_();
  2591. }
  2592.  
  2593. /**
  2594.  * Invoked to make this displayer active. The displayer will now start
  2595.  * responding to notifications such as commands and resize events. We
  2596.  * can't do this in the constructor because there might be many 
  2597.  * displayers instantiated waiting in the problem queue for a particular
  2598.  * browser (e.g., a page has multiple framed problem pages), and we
  2599.  * don't want them all responding to commands!
  2600.  *
  2601.  * Invoked zero (the page we're a warning for was nav'd away from
  2602.  * before it reaches the head of the problem queue) or one (we're
  2603.  * displaying this warning) times by the browser view.
  2604.  */
  2605. PROT_PhishMsgDisplayerBase.prototype.start = function() {
  2606.   G_Assert(this, this.started_ == undefined, "Displayer started twice?");
  2607.   this.started_ = true;
  2608.  
  2609.   this.commandController_ = new PROT_CommandController(this.commandHandlers_);
  2610.   this.doc_.defaultView.controllers.appendController(this.commandController_);
  2611.  
  2612.   // Add an event listener for when the browser resizes (e.g., user
  2613.   // shows/hides the sidebar).
  2614.   this.resizeHandler_ = BindToObject(this.onBrowserResized_, this);
  2615.   this.browser_.addEventListener("resize",
  2616.                                  this.resizeHandler_, 
  2617.                                  false);
  2618. }
  2619.  
  2620. /**
  2621.  * @returns Boolean indicating whether this displayer is currently
  2622.  *          active
  2623.  */
  2624. PROT_PhishMsgDisplayerBase.prototype.isActive = function() {
  2625.   return !!this.started_;
  2626. }
  2627.  
  2628. /**
  2629.  * Invoked by the browser view to clean up after the user is done 
  2630.  * interacting with the message. Should be called once by the browser
  2631.  * view. 
  2632.  */
  2633. PROT_PhishMsgDisplayerBase.prototype.done = function() {
  2634.   G_Assert(this, !this.done_, "Called done more than once?");
  2635.   this.done_ = true;
  2636.  
  2637.   // If the Document we're showing the warning for was nav'd away from
  2638.   // before we had a chance to get started, we have nothing to do.
  2639.   if (this.started_) {
  2640.  
  2641.     // If we were started, we must be the current problem, so these things
  2642.     // must be showing
  2643.     this.removeWarningInUrlbar_();
  2644.     this.unhideLockIcon_();
  2645.  
  2646.     // Could be though that they've closed the warning dialog
  2647.     if (this.messageShowing_)
  2648.       this.hideMessage_();
  2649.  
  2650.     if (this.resizeHandler_) {
  2651.       this.browser_.removeEventListener("resize", 
  2652.                                         this.resizeHandler_, 
  2653.                                         false);
  2654.       this.resizeHandler_ = null;
  2655.     }
  2656.     
  2657.     var win = this.doc_.defaultView;
  2658.     win.controllers.removeController(this.commandController_);
  2659.     this.commandController_ = null;
  2660.   }
  2661. }
  2662.  
  2663. /**
  2664.  * Helper function to remove a substring from inside a string.
  2665.  *
  2666.  * @param orig String to remove substring from
  2667.  * 
  2668.  * @param toRemove String to remove (if it is present)
  2669.  *
  2670.  * @returns String with the substring removed
  2671.  */
  2672. PROT_PhishMsgDisplayerBase.prototype.removeIfExists_ = function(orig,
  2673.                                                                 toRemove) {
  2674.   var pos = orig.indexOf(toRemove);
  2675.   if (pos != -1)
  2676.     orig = orig.substring(0, pos) + orig.substring(pos + toRemove.length);
  2677.  
  2678.   return orig;
  2679. }
  2680.  
  2681. /**
  2682.  * We don't want to confuse users if they land on a phishy page that uses
  2683.  * SSL, so ensure that the lock icon never shows when we're showing our 
  2684.  * warning.
  2685.  */
  2686. PROT_PhishMsgDisplayerBase.prototype.hideLockIcon_ = function() {
  2687.   var lockIcon = this.doc_.getElementById("lock-icon");
  2688.   if (!lockIcon)
  2689.     return;
  2690.   lockIcon.hidden = true;
  2691. }
  2692.  
  2693. /**
  2694.  * Ensure they can see it after our warning is finished.
  2695.  */
  2696. PROT_PhishMsgDisplayerBase.prototype.unhideLockIcon_ = function() {
  2697.   var lockIcon = this.doc_.getElementById("lock-icon");
  2698.   if (!lockIcon)
  2699.     return;
  2700.   lockIcon.hidden = false;
  2701. }
  2702.  
  2703. /**
  2704.  * This method makes our warning icon visible in the location bar. It will
  2705.  * be removed only when the problematic document is navigated awy from 
  2706.  * (i.e., when done() is called), and not when the warning is dismissed.
  2707.  */
  2708. PROT_PhishMsgDisplayerBase.prototype.addWarningInUrlbar_ = function() {
  2709.   var urlbarIcon = this.doc_.getElementById(this.urlbarIconId_);
  2710.   if (!urlbarIcon)
  2711.     return;
  2712.   urlbarIcon.setAttribute('level', 'warn');
  2713. }
  2714.  
  2715. /**
  2716.  * Hides our urlbar icon
  2717.  */
  2718. PROT_PhishMsgDisplayerBase.prototype.removeWarningInUrlbar_ = function() {
  2719.   var urlbarIcon = this.doc_.getElementById(this.urlbarIconId_);
  2720.   if (!urlbarIcon)
  2721.     return;
  2722.   urlbarIcon.setAttribute('level', 'safe');
  2723. }
  2724.  
  2725. /**
  2726.  * VIRTUAL -- Displays the warning message
  2727.  */
  2728. PROT_PhishMsgDisplayerBase.prototype.showMessage_ = function() { };
  2729.  
  2730. /**
  2731.  * VIRTUAL -- Hide the warning message from the user.
  2732.  */
  2733. PROT_PhishMsgDisplayerBase.prototype.hideMessage_ = function() { };
  2734.  
  2735. /**
  2736.  * Reposition the message relative to refElement in the parent window
  2737.  *
  2738.  * @param message Reference to the element to position
  2739.  * @param tail Reference to the message tail
  2740.  * @param refElement Reference to element relative to which we position
  2741.  *                   ourselves
  2742.  */
  2743. PROT_PhishMsgDisplayerBase.prototype.adjustLocation_ = function(message,
  2744.                                                                 tail,
  2745.                                                                 refElement) {
  2746.   var refX = refElement.boxObject.x;
  2747.   var refY = refElement.boxObject.y;
  2748.   var refHeight = refElement.boxObject.height;
  2749.   var refWidth = refElement.boxObject.width;
  2750.   G_Debug(this, "Ref element is at [window-relative] (" + refX + ", " + 
  2751.           refY + ")");
  2752.  
  2753.   var pixelsIntoRefY = -2;
  2754.   var tailY = refY + refHeight - pixelsIntoRefY;
  2755.   var tailPixelsLeftOfRefX = tail.boxObject.width;
  2756.   var tailPixelsIntoRefX = Math.round(refWidth / 2);
  2757.   var tailX = refX - tailPixelsLeftOfRefX + tailPixelsIntoRefX;
  2758.  
  2759.   // Move message up a couple pixels so the tail overlaps it.
  2760.   var messageY = tailY + tail.boxObject.height - 2;
  2761.   var messagePixelsLeftOfRefX = 375;
  2762.   var messageX = refX - messagePixelsLeftOfRefX;
  2763.   G_Debug(this, "Message is at [window-relative] (" + messageX + ", " + 
  2764.           messageY + ")");
  2765.   G_Debug(this, "Tail is at [window-relative] (" + tailX + ", " + 
  2766.           tailY + ")");
  2767.  
  2768.   if (messageX < 0) {
  2769.     // We're hanging off the left edge, switch to floating mode
  2770.     tail.style.display = "none";
  2771.     this.adjustLocationFloating_(message);
  2772.     return;
  2773.   }
  2774.  
  2775.   tail.style.top = tailY + "px";
  2776.   tail.style.left = tailX + "px";
  2777.   message.style.top = messageY + "px";
  2778.   message.style.left = messageX + "px";
  2779.   
  2780.   this.maybeAddScrollbars_();
  2781. }
  2782.  
  2783. /**
  2784.  * Position the warning bubble with no reference element.  In this case we
  2785.  * just center the warning bubble at the top of the users window.
  2786.  * @param message XULElement message bubble XUL container.
  2787.  */
  2788. PROT_PhishMsgDisplayerBase.prototype.adjustLocationFloating_ = function(message) {
  2789.   // Compute X offset
  2790.   var browserX = this.browser_.boxObject.x;
  2791.   var browserXCenter = browserX + this.browser_.boxObject.width / 2;
  2792.   var messageX = browserXCenter - (message.boxObject.width / 2);
  2793.  
  2794.   // Compute Y offset (top of the browser window)
  2795.   var messageY = this.browser_.boxObject.y;
  2796.  
  2797.   // Position message
  2798.   message.style.top = messageY + "px";
  2799.   message.style.left = messageX + "px";
  2800.  
  2801.   this.maybeAddScrollbars_();
  2802. }
  2803.  
  2804. /**
  2805.  * Add a vertical scrollbar if we're falling out of the browser window.
  2806.  */
  2807. PROT_PhishMsgDisplayerBase.prototype.maybeAddScrollbars_ = function() {
  2808.   var message = this.doc_.getElementById(this.messageId_);
  2809.   
  2810.   var content = this.doc_.getElementById(this.messageContentId_);
  2811.   var bottom = content.boxObject.y + content.boxObject.height;
  2812.   var maxY = this.doc_.defaultView.innerHeight;
  2813.   G_Debug(this, "bottom: " + bottom + ", maxY: " + maxY
  2814.                 + ", new height: " + (maxY - content.boxObject.y));
  2815.   if (bottom > maxY) {
  2816.     var newHeight = maxY - content.boxObject.y;
  2817.     if (newHeight < 1)
  2818.       newHeight = 1;
  2819.  
  2820.     content.style.height = newHeight + "px";
  2821.     content.style.overflow = "auto";
  2822.   }
  2823. }
  2824.  
  2825. /**
  2826.  * Show the extended warning message
  2827.  */
  2828. PROT_PhishMsgDisplayerBase.prototype.showMore_ = function() {
  2829.   this.doc_.getElementById(this.extendedMessageId_).hidden = false;
  2830.   this.doc_.getElementById(this.showmoreLinkId_).style.display = "none";
  2831.  
  2832.   // set FAQ URL
  2833.   var formatter = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  2834.                             .getService(Components.interfaces.nsIURLFormatter);
  2835.   var faqURL = formatter.formatURLPref("browser.safebrowsing.warning.infoURL");
  2836.   var labelEl = this.doc_.getElementById(this.faqLinkId_);
  2837.   labelEl.setAttribute("href", faqURL);
  2838.   
  2839.   this.maybeAddScrollbars_();
  2840. }
  2841.  
  2842. /**
  2843.  * The user clicked on one of the links in the buble.  Display the
  2844.  * corresponding page in a new window with all the chrome enabled.
  2845.  *
  2846.  * @param url The URL to display in a new window
  2847.  */
  2848. PROT_PhishMsgDisplayerBase.prototype.showURL_ = function(url) {
  2849.   this.windowWatcher_.openWindow(this.windowWatcher_.activeWindow,
  2850.                                  url,
  2851.                                  "_blank",
  2852.                                  null,
  2853.                                  null);
  2854. }
  2855.  
  2856. /**
  2857.  * If the warning bubble came up in error, this url goes to a form
  2858.  * to notify the data provider.
  2859.  * @return url String
  2860.  */
  2861. PROT_PhishMsgDisplayerBase.prototype.getReportErrorURL_ = function() {
  2862.   var badUrl = this.url_;
  2863.  
  2864.   var url = gDataProvider.getReportErrorURL();
  2865.   url += "&url=" + encodeURIComponent(badUrl);
  2866.   return url;
  2867. }
  2868.  
  2869. /**
  2870.  * URL for the user to report back to us.  This is to provide the user
  2871.  * with an action after being warned.
  2872.  */
  2873. PROT_PhishMsgDisplayerBase.prototype.getReportGenericURL_ = function() {
  2874.   var badUrl = this.url_;
  2875.  
  2876.   var url = gDataProvider.getReportGenericURL();
  2877.   url += "&url=" + encodeURIComponent(badUrl);
  2878.   return url;
  2879. }
  2880.  
  2881.  
  2882. /**
  2883.  * A specific implementation of the dislpayer using a canvas. This
  2884.  * class is meant for use on platforms that don't support transparent
  2885.  * elements over browser content (currently: all platforms). 
  2886.  *
  2887.  * The main ugliness is the fact that we're hiding the content area and
  2888.  * painting the page to canvas. As a result, we must periodically
  2889.  * re-paint the canvas to reflect updates to the page. Otherwise if
  2890.  * the page was half-loaded when we showed our warning, it would
  2891.  * stay that way even though the page actually finished loading. 
  2892.  *
  2893.  * See base constructor for full details of constructor args.
  2894.  *
  2895.  * @constructor
  2896.  */
  2897. function PROT_PhishMsgDisplayerCanvas(msgDesc, browser, doc, url) {
  2898.   PROT_PhishMsgDisplayerBase.call(this, msgDesc, browser, doc, url);
  2899.  
  2900.   this.dimAreaId_ = "safebrowsing-dim-area-canvas";
  2901.   this.pageCanvasId_ = "safebrowsing-page-canvas";
  2902.   this.xhtmlNS_ = "http://www.w3.org/1999/xhtml";     // we create html:canvas
  2903. }
  2904.  
  2905. PROT_PhishMsgDisplayerCanvas.inherits(PROT_PhishMsgDisplayerBase);
  2906.  
  2907. /**
  2908.  * Displays the warning message.  First we make sure the overlay is loaded
  2909.  * then call showMessageAfterOverlay_.
  2910.  */
  2911. PROT_PhishMsgDisplayerCanvas.prototype.showMessage_ = function() {
  2912.   G_Debug(this, "Showing message.");
  2913.  
  2914.   // Load the overlay if we haven't already.
  2915.   var dimmer = this.doc_.getElementById('safebrowsing-dim-area-canvas');
  2916.   if (!dimmer) {
  2917.     var onOverlayMerged = BindToObject(this.showMessageAfterOverlay_,
  2918.                                        this);
  2919.     var observer = new G_ObserverWrapper("xul-overlay-merged",
  2920.                                          onOverlayMerged);
  2921.  
  2922.     this.doc_.loadOverlay(
  2923.         "chrome://browser/content/safebrowsing/warning-overlay.xul",
  2924.         observer);
  2925.   } else {
  2926.     // The overlay is already loaded so we go ahead and call
  2927.     // showMessageAfterOverlay_.
  2928.     this.showMessageAfterOverlay_();
  2929.   }
  2930. }
  2931.  
  2932. /**
  2933.  * This does the actual work of showing the warning message.
  2934.  */
  2935. PROT_PhishMsgDisplayerCanvas.prototype.showMessageAfterOverlay_ = function() {
  2936.   this.messageShowing_ = true;
  2937.  
  2938.   // Position the canvas overlay. Order here is significant, but don't ask me
  2939.   // why for some of these. You need to:
  2940.   // 1. get browser dimensions
  2941.   // 2. add canvas to the document
  2942.   // 3. unhide the dimmer (gray out overlay)
  2943.   // 4. display to the canvas
  2944.   // 5. unhide the warning message
  2945.   // 6. update link targets in warning message
  2946.   // 7. focus the warning message
  2947.  
  2948.   // (1)
  2949.   var w = this.browser_.boxObject.width;
  2950.   G_Debug(this, "browser w=" + w);
  2951.   var h = this.browser_.boxObject.height;
  2952.   G_Debug(this, "browser h=" + h);
  2953.   var x = this.browser_.boxObject.x;
  2954.   G_Debug(this, "browser x=" + w);
  2955.   var y = this.browser_.boxObject.y;
  2956.   G_Debug(this, "browser y=" + h);
  2957.  
  2958.   var win = this.browser_.contentWindow;
  2959.   var scrollX = win.scrollX;
  2960.   G_Debug(this, "win scrollx=" + scrollX);
  2961.   var scrollY = win.scrollY;
  2962.   G_Debug(this, "win scrolly=" + scrollY);
  2963.  
  2964.   // (2)
  2965.   // We add the canvas dynamically and remove it when we're done because
  2966.   // leaving it hanging around consumes a lot of memory.
  2967.   var pageCanvas = this.doc_.createElementNS(this.xhtmlNS_, "html:canvas");
  2968.   pageCanvas.id = this.pageCanvasId_;
  2969.   pageCanvas.style.left = x + 'px';
  2970.   pageCanvas.style.top = y + 'px';
  2971.  
  2972.   var dimarea = this.doc_.getElementById(this.dimAreaId_);
  2973.   this.doc_.getElementById('main-window').insertBefore(pageCanvas,
  2974.                                                        dimarea);
  2975.  
  2976.   // (3)
  2977.   dimarea.style.left = x + 'px';
  2978.   dimarea.style.top = y + 'px';
  2979.   dimarea.style.width = w + 'px';
  2980.   dimarea.style.height = h + 'px';
  2981.   dimarea.hidden = false;
  2982.   
  2983.   // (4)
  2984.   pageCanvas.setAttribute("width", w);
  2985.   pageCanvas.setAttribute("height", h);
  2986.  
  2987.   var bgcolor = this.getBackgroundColor_();
  2988.  
  2989.   var cx = pageCanvas.getContext("2d");
  2990.   cx.drawWindow(win, scrollX, scrollY, w, h, bgcolor);
  2991.  
  2992.   // Now repaint the window every so often in case the content hasn't fully
  2993.   // loaded at this point.
  2994.   var debZone = this.debugZone;
  2995.   function repaint() {
  2996.     G_Debug(debZone, "Repainting canvas...");
  2997.     cx.drawWindow(win, scrollX, scrollY, w, h, bgcolor);
  2998.   };
  2999.   this.repainter_ = new PROT_PhishMsgCanvasRepainter(repaint);
  3000.  
  3001.   // (5)
  3002.   this.showAndPositionWarning_();
  3003.  
  3004.   // (6)
  3005.   var link = this.doc_.getElementById('safebrowsing-palm-falsepositive-link');
  3006.   link.href = this.getReportErrorURL_();
  3007.  
  3008.   // (7)
  3009.   this.doc_.getElementById(this.messageContentId_).focus();
  3010. }
  3011.  
  3012. /**
  3013.  * Show and position the warning message.  We position the waring message
  3014.  * relative to the icon in the url bar, but if the element doesn't exist,
  3015.  * (e.g., the user remove the url bar from her/his chrome), we anchor at the
  3016.  * top of the window.
  3017.  */
  3018. PROT_PhishMsgDisplayerCanvas.prototype.showAndPositionWarning_ = function() {
  3019.   var refElement = this.doc_.getElementById(this.refElementId_);
  3020.   var message = this.doc_.getElementById(this.messageId_);
  3021.   var tail = this.doc_.getElementById(this.messageTailId_);
  3022.  
  3023.   message.hidden = false;
  3024.   message.style.display = "block";
  3025.  
  3026.   // Determine if the refElement is visible.
  3027.   if (this.isVisibleElement_(refElement)) {
  3028.     // Show tail and position warning relative to refElement.
  3029.     tail.hidden = false;
  3030.     tail.style.display = "block";
  3031.     this.adjustLocation_(message, tail, refElement);
  3032.   } else {
  3033.     // No ref element, position in the top center of window.
  3034.     tail.hidden = true;
  3035.     tail.style.display = "none";
  3036.     this.adjustLocationFloating_(message);
  3037.   }
  3038. }
  3039.  
  3040. /**
  3041.  * @return Boolean true if elt is a visible XUL element.
  3042.  */
  3043. PROT_PhishMsgDisplayerCanvas.prototype.isVisibleElement_ = function(elt) {
  3044.   if (!elt)
  3045.     return false;
  3046.   
  3047.   // If it's on a collapsed/hidden toolbar, the x position is set to 0.
  3048.   if (elt.boxObject.x == 0)
  3049.     return false;
  3050.  
  3051.   return true;
  3052. }
  3053.  
  3054. /**
  3055.  * Hide the warning message from the user.
  3056.  */
  3057. PROT_PhishMsgDisplayerCanvas.prototype.hideMessage_ = function() {
  3058.   G_Debug(this, "Hiding phishing warning.");
  3059.   G_Assert(this, this.messageShowing_, "Hide message called but not showing?");
  3060.  
  3061.   this.messageShowing_ = false;
  3062.   this.repainter_.cancel();
  3063.   this.repainter_ = null;
  3064.  
  3065.   // Hide the warning popup.
  3066.   var message = this.doc_.getElementById(this.messageId_);
  3067.   message.hidden = true;
  3068.   message.style.display = "none";
  3069.   var content = this.doc_.getElementById(this.messageContentId_);
  3070.   content.style.height = "";
  3071.   content.style.overflow = "";
  3072.  
  3073.   var tail = this.doc_.getElementById(this.messageTailId_);
  3074.   tail.hidden = true;
  3075.   tail.style.display = "none";
  3076.  
  3077.   // Remove the canvas element from the chrome document.
  3078.   var pageCanvas = this.doc_.getElementById(this.pageCanvasId_);
  3079.   pageCanvas.parentNode.removeChild(pageCanvas);
  3080.  
  3081.   // Hide the dimmer.
  3082.   var dimarea = this.doc_.getElementById(this.dimAreaId_);
  3083.   dimarea.hidden = true;
  3084. }
  3085.  
  3086.  
  3087. /**
  3088.  * Helper class that periodically repaints the canvas. We repaint
  3089.  * frequently at first, and then back off to a less frequent schedule
  3090.  * at "steady state," and finally just stop altogether. We have to do
  3091.  * this because we're not sure if the page has finished loading when
  3092.  * we first paint the canvas, and because we want to reflect any
  3093.  * dynamically written content into the canvas as it appears in the
  3094.  * page after load.
  3095.  *
  3096.  * @param repaintFunc Function to call to repaint browser.
  3097.  *
  3098.  * @constructor
  3099.  */
  3100. function PROT_PhishMsgCanvasRepainter(repaintFunc) {
  3101.   this.count_ = 0;
  3102.   this.repaintFunc_ = repaintFunc;
  3103.   this.initPeriodMS_ = 500;             // Initially repaint every 500ms
  3104.   this.steadyStateAtMS_ = 10 * 1000;    // Go slowly after 10 seconds,
  3105.   this.steadyStatePeriodMS_ = 3 * 1000; // repainting every 3 seconds, and
  3106.   this.quitAtMS_ = 20 * 1000;           // stop after 20 seconds
  3107.   this.startMS_ = (new Date).getTime();
  3108.   this.alarm_ = new G_Alarm(BindToObject(this.repaint, this), 
  3109.                             this.initPeriodMS_);
  3110. }
  3111.  
  3112. /**
  3113.  * Called periodically to repaint the canvas
  3114.  */
  3115. PROT_PhishMsgCanvasRepainter.prototype.repaint = function() {
  3116.   this.repaintFunc_();
  3117.  
  3118.   var nextRepaint;
  3119.   // If we're in "steady state", use the slow repaint rate, else fast
  3120.   if ((new Date).getTime() - this.startMS_ > this.steadyStateAtMS_)
  3121.     nextRepaint = this.steadyStatePeriodMS_;
  3122.   else 
  3123.     nextRepaint = this.initPeriodMS_;
  3124.  
  3125.   if (!((new Date).getTime() - this.startMS_ > this.quitAtMS_))
  3126.     this.alarm_ = new G_Alarm(BindToObject(this.repaint, this), nextRepaint);
  3127. }
  3128.  
  3129. /**
  3130.  * Called to stop repainting the canvas
  3131.  */
  3132. PROT_PhishMsgCanvasRepainter.prototype.cancel = function() {
  3133.   if (this.alarm_) {
  3134.     this.alarm_.cancel();
  3135.     this.alarm_ = null;
  3136.   }
  3137.   this.repaintFunc_ = null;
  3138. }
  3139. /* ***** BEGIN LICENSE BLOCK *****
  3140.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3141.  *
  3142.  * The contents of this file are subject to the Mozilla Public License Version
  3143.  * 1.1 (the "License"); you may not use this file except in compliance with
  3144.  * the License. You may obtain a copy of the License at
  3145.  * http://www.mozilla.org/MPL/
  3146.  *
  3147.  * Software distributed under the License is distributed on an "AS IS" basis,
  3148.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3149.  * for the specific language governing rights and limitations under the
  3150.  * License.
  3151.  *
  3152.  * The Original Code is Google Safe Browsing.
  3153.  *
  3154.  * The Initial Developer of the Original Code is Google Inc.
  3155.  * Portions created by the Initial Developer are Copyright (C) 2006
  3156.  * the Initial Developer. All Rights Reserved.
  3157.  *
  3158.  * Contributor(s):
  3159.  *   Fritz Schneider <fritz@google.com> (original author)
  3160.  *
  3161.  * Alternatively, the contents of this file may be used under the terms of
  3162.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3163.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3164.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3165.  * of those above. If you wish to allow use of your version of this file only
  3166.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3167.  * use your version of this file under the terms of the MPL, indicate your
  3168.  * decision by deleting the provisions above and replace them with the notice
  3169.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3170.  * the provisions above, a recipient may use your version of this file under
  3171.  * the terms of any one of the MPL, the GPL or the LGPL.
  3172.  *
  3173.  * ***** END LICENSE BLOCK ***** */
  3174.  
  3175.  
  3176. // The warden checks request to see if they are for phishy pages. It
  3177. // does so by either querying a remote server with the URL (advanced
  3178. // protectoin mode) or querying our locally stored blacklists (privacy
  3179. // mode).
  3180. // 
  3181. // When the warden notices a problem, it queries all browser views
  3182. // (each of which corresopnds to an open browser window) to see
  3183. // whether one of them can handle it. A browser view can handle a
  3184. // problem if its browser window has an HTMLDocument loaded with the
  3185. // given URL and that Document hasn't already been flagged as a
  3186. // problem. For every problematic URL we notice loading, at most one
  3187. // Document is flagged as problematic. Otherwise you can get into
  3188. // trouble if multiple concurrent phishy pages load with the same URL.
  3189. //
  3190. // Since we check URLs very early in the request cycle (in a progress
  3191. // listener), the URL might not yet be associated with a Document when
  3192. // we determine that it is phishy. So the the warden retries finding
  3193. // a browser view to handle the problem until one can, or until it
  3194. // determines it should give up (see complicated logic below).
  3195. //
  3196. // The warden has displayers that the browser view uses to render
  3197. // different kinds of warnings (e.g., one that's shown before a page
  3198. // loads as opposed to one that's shown after the page has already
  3199. // loaded).
  3200. //
  3201. // Note: There is a single warden for the whole application.
  3202. //
  3203. // TODO better way to expose displayers/views to browser view
  3204.  
  3205. const kPhishWardenEnabledPref = "browser.safebrowsing.enabled";
  3206. const kPhishWardenRemoteLookups = "browser.safebrowsing.remoteLookups";
  3207.  
  3208. // We have hardcoded URLs that we let people navigate to in order to 
  3209. // check out the warning.
  3210. const kTestUrls = {
  3211.   "http://www.google.com/tools/firefox/safebrowsing/phish-o-rama.html": true,
  3212.   "http://www.mozilla.org/projects/bonecho/anti-phishing/its-a-trap.html": true,
  3213.   "http://www.mozilla.com/firefox/its-a-trap.html": true,
  3214. }
  3215.  
  3216. /**
  3217.  * Abtracts the checking of user/browser actions for signs of
  3218.  * phishing. 
  3219.  *
  3220.  * @param progressListener nsIDocNavStartProgressListener
  3221.  * @constructor
  3222.  */
  3223. function PROT_PhishingWarden(progressListener) {
  3224.   PROT_ListWarden.call(this);
  3225.  
  3226.   this.debugZone = "phishwarden";
  3227.   this.testing_ = false;
  3228.   this.browserViews_ = [];
  3229.  
  3230.   // Use this to query preferences
  3231.   this.prefs_ = new G_Preferences();
  3232.  
  3233.   // Only one displayer so far; perhaps we'll have others in the future
  3234.   this.displayers_ = {
  3235.     "afterload": PROT_PhishMsgDisplayer,
  3236.   };
  3237.  
  3238.   // We use this dude to do lookups on our remote server
  3239.   this.fetcher_ = new PROT_TRFetcher();
  3240.  
  3241.   // We need to know whether we're enabled and whether we're in advanced
  3242.   // mode, so reflect the appropriate preferences into our state.
  3243.  
  3244.   // Read state: should we be checking remote preferences?
  3245.   this.checkRemote_ = this.prefs_.getPref(kPhishWardenRemoteLookups, null);
  3246.   
  3247.   // true if we should use whitelists to suppress remote lookups
  3248.   this.checkWhitelists_ = false;
  3249.  
  3250.   // Get notifications when the remote check preference changes
  3251.   var checkRemotePrefObserver = BindToObject(this.onCheckRemotePrefChanged,
  3252.                                              this);
  3253.   this.prefs_.addObserver(kPhishWardenRemoteLookups, checkRemotePrefObserver);
  3254.  
  3255.   // Global preference to enable the phishing warden
  3256.   this.phishWardenEnabled_ = this.prefs_.getPref(kPhishWardenEnabledPref, null);
  3257.  
  3258.   // Get notifications when the phishing warden enabled pref changes
  3259.   var phishWardenPrefObserver = 
  3260.     BindToObject(this.onPhishWardenEnabledPrefChanged, this);
  3261.   this.prefs_.addObserver(kPhishWardenEnabledPref, phishWardenPrefObserver);
  3262.   
  3263.   // Get notifications when the data provider pref changes
  3264.   var dataProviderPrefObserver =
  3265.     BindToObject(this.onDataProviderPrefChanged, this);
  3266.   this.prefs_.addObserver(kDataProviderIdPref, dataProviderPrefObserver);
  3267.  
  3268.   // hook up our browser listener
  3269.   this.progressListener_ = progressListener;
  3270.   this.progressListener_.callback = this;
  3271.   this.progressListener_.enabled = this.phishWardenEnabled_;
  3272.   // ms to wait after a request has started before firing JS callback
  3273.   this.progressListener_.delay = 1500;
  3274.  
  3275.   // object to keep track of request errors if we're in remote check mode
  3276.   this.requestBackoff_ = new RequestBackoff(3 /* num errors */,
  3277.                                    10*60*1000 /* error time, 10min */,
  3278.                                    10*60*1000 /* backoff interval, 10min */,
  3279.                                    6*60*60*1000 /* max backoff, 6hr */);
  3280.  
  3281.   G_Debug(this, "phishWarden initialized");
  3282. }
  3283.  
  3284. PROT_PhishingWarden.inherits(PROT_ListWarden);
  3285.  
  3286. /**
  3287.  * We implement nsIWebProgressListener
  3288.  */
  3289. PROT_PhishingWarden.prototype.QueryInterface = function(iid) {
  3290.   if (iid.equals(Ci.nsISupports) || 
  3291.       iid.equals(Ci.nsIWebProgressListener) ||
  3292.       iid.equals(Ci.nsISupportsWeakReference))
  3293.     return this;
  3294.   throw Components.results.NS_ERROR_NO_INTERFACE;
  3295. }
  3296.  
  3297. /**
  3298.  * Cleanup on shutdown.
  3299.  */
  3300. PROT_PhishingWarden.prototype.shutdown = function() {
  3301.   this.progressListener_.callback = null;
  3302.   this.progressListener_ = null;
  3303.   this.listManager_ = null;
  3304. }
  3305.  
  3306. /**
  3307.  * When a preference (either advanced features or the phishwarden
  3308.  * enabled) changes, we might have to start or stop asking for updates. 
  3309.  * 
  3310.  * This is a little tricky; we start or stop management only when we
  3311.  * have complete information we can use to determine whether we
  3312.  * should.  It could be the case that one pref or the other isn't set
  3313.  * yet (e.g., they haven't opted in/out of advanced features). So do
  3314.  * nothing unless we have both pref values -- we get notifications for
  3315.  * both, so eventually we will start correctly.
  3316.  */ 
  3317. PROT_PhishingWarden.prototype.maybeToggleUpdateChecking = function() {
  3318.   if (this.testing_)
  3319.     return;
  3320.  
  3321.   var phishWardenEnabled = this.prefs_.getPref(kPhishWardenEnabledPref, null);
  3322.  
  3323.   this.checkRemote_ = this.prefs_.getPref(kPhishWardenRemoteLookups, null);
  3324.  
  3325.   G_Debug(this, "Maybe toggling update checking. " +
  3326.           "Warden enabled? " + phishWardenEnabled + " || " +
  3327.           "Check remote? " + this.checkRemote_);
  3328.  
  3329.   // Do nothing unless both prefs are set.  They can be null (unset), true, or
  3330.   // false.
  3331.   if (phishWardenEnabled === null || this.checkRemote_ === null)
  3332.     return;
  3333.  
  3334.   // We update and save to disk all tables if we don't have remote checking
  3335.   // enabled.
  3336.   if (phishWardenEnabled === true) {
  3337.     // If anti-phishing is enabled, we always download the local files to
  3338.     // use in case remote lookups fail.
  3339.     this.enableBlacklistTableUpdates();
  3340.     this.enableWhitelistTableUpdates();
  3341.  
  3342.     if (this.checkRemote_ === true) {
  3343.       // Remote lookup mode
  3344.       // We check to see if the local list update host is the same as the
  3345.       // remote lookup host.  If they are the same, then we don't bother
  3346.       // to do a remote url check if the url is in the whitelist.
  3347.       var ioService = Cc["@mozilla.org/network/io-service;1"]
  3348.                      .getService(Ci.nsIIOService);
  3349.       var updateHost = '';
  3350.       var lookupHost = '';
  3351.       try {
  3352.         var url = ioService.newURI(gDataProvider.getUpdateURL(),
  3353.                                          null, null);
  3354.         updateHost = url.asciiHost;
  3355.       } catch (e) { }
  3356.       try {
  3357.         var url = ioService.newURI(gDataProvider.getLookupURL(),
  3358.                                          null, null);
  3359.         lookupHost = url.asciiHost;
  3360.       } catch (e) { }
  3361.  
  3362.       if (updateHost && lookupHost && updateHost == lookupHost) {
  3363.         // The data provider for local lists and remote lookups is the
  3364.         // same, enable whitelist lookup suppression.
  3365.         this.checkWhitelists_ = true;
  3366.       } else {
  3367.         // hosts don't match, don't use whitelist suppression
  3368.         this.checkWhitelists_ = false;
  3369.       }
  3370.     }
  3371.   } else {
  3372.     // Anti-phishing is off, disable table updates
  3373.     this.disableBlacklistTableUpdates();
  3374.     this.disableWhitelistTableUpdates();
  3375.   }
  3376. }
  3377.  
  3378. /**
  3379.  * Controllers register their browser views with us
  3380.  *
  3381.  * @param view Reference to a browser view 
  3382.  */
  3383. PROT_PhishingWarden.prototype.addBrowserView = function(view) {
  3384.   G_Debug(this, "New browser view registered.");
  3385.   this.browserViews_.push(view);
  3386. }
  3387.  
  3388. /**
  3389.  * Controllers unregister their views when their window closes
  3390.  *
  3391.  * @param view Reference to a browser view 
  3392.  */
  3393. PROT_PhishingWarden.prototype.removeBrowserView = function(view) {
  3394.   for (var i = 0; i < this.browserViews_.length; i++)
  3395.     if (this.browserViews_[i] === view) {
  3396.       G_Debug(this, "Browser view unregistered.");
  3397.       this.browserViews_.splice(i, 1);
  3398.       return;
  3399.     }
  3400.   G_Assert(this, false, "Tried to unregister non-existent browser view!");
  3401. }
  3402.  
  3403. /**
  3404.  * Deal with a user changing the pref that says whether we should check
  3405.  * the remote server (i.e., whether we're in advanced mode)
  3406.  *
  3407.  * @param prefName Name of the pref holding the value indicating whether
  3408.  *                 we should check remote server
  3409.  */
  3410. PROT_PhishingWarden.prototype.onCheckRemotePrefChanged = function(prefName) {
  3411.   this.checkRemote_ = this.prefs_.getBoolPrefOrDefault(prefName,
  3412.                                                        this.checkRemote_);
  3413.   this.requestBackoff_.reset();
  3414.   this.maybeToggleUpdateChecking();
  3415. }
  3416.  
  3417. /**
  3418.  * Deal with a user changing the pref that says whether we should 
  3419.  * enable the phishing warden (i.e., that SafeBrowsing is active)
  3420.  *
  3421.  * @param prefName Name of the pref holding the value indicating whether
  3422.  *                 we should enable the phishing warden
  3423.  */
  3424. PROT_PhishingWarden.prototype.onPhishWardenEnabledPrefChanged = function(
  3425.                                                                     prefName) {
  3426.   this.phishWardenEnabled_ = 
  3427.     this.prefs_.getBoolPrefOrDefault(prefName, this.phishWardenEnabled_);
  3428.   this.requestBackoff_.reset();
  3429.   this.maybeToggleUpdateChecking();
  3430.   this.progressListener_.enabled = this.phishWardenEnabled_;
  3431. }
  3432.  
  3433. /**
  3434.  * Event fired when the user changes data providers.
  3435.  */
  3436. PROT_PhishingWarden.prototype.onDataProviderPrefChanged = function(prefName) {
  3437.   // We want to reset request backoff state since it's a different provider.
  3438.   this.requestBackoff_.reset();
  3439.  
  3440.   // If we have a new data provider and we're doing remote lookups, then
  3441.   // we may want to use whitelist lookup suppression or change which
  3442.   // tables are being downloaded.
  3443.   if (this.checkRemote_) {
  3444.     this.maybeToggleUpdateChecking();
  3445.   }
  3446. }
  3447.  
  3448. /**
  3449.  * A request for a Document has been initiated somewhere. Check it!
  3450.  *
  3451.  * @param request
  3452.  * @param url
  3453.  */ 
  3454. PROT_PhishingWarden.prototype.onDocNavStart = function(request, url) {
  3455.   G_Debug(this, "checkRemote: " +
  3456.           (this.checkRemote_ ? "yes" : "no"));
  3457.  
  3458.   // If we're on a test page, trigger the warning.
  3459.   // XXX Do we still need a test url or should each provider just put
  3460.   // it in their local list?
  3461.   if (this.isBlacklistTestURL(url)) {
  3462.     this.houstonWeHaveAProblem_(request);
  3463.     return;
  3464.   }
  3465.  
  3466.   // Make a remote lookup check if the pref is selected and if we haven't
  3467.   // triggered server backoff.  Otherwise, make a local check.
  3468.   if (this.checkRemote_ && this.requestBackoff_.canMakeRequest()) {
  3469.     // If we can use whitelists to suppress remote lookups, do so.
  3470.     if (this.checkWhitelists_) {
  3471.       var maybeRemoteCheck = BindToObject(this.maybeMakeRemoteCheck_,
  3472.                                           this,
  3473.                                           url,
  3474.                                           request);
  3475.       this.isWhiteURL(url, maybeRemoteCheck);
  3476.     } else {
  3477.       // Do a remote lookup (don't check whitelists)
  3478.       this.fetcher_.get(url,
  3479.                         BindToObject(this.onTRFetchComplete,
  3480.                                      this,
  3481.                                      url,
  3482.                                      request));
  3483.     }
  3484.   } else {
  3485.     // Check the local lists for a match.
  3486.     var evilCallback = BindToObject(this.localListMatch_,
  3487.                                     this,
  3488.                                     url,
  3489.                                     request);
  3490.     this.isEvilURL(url, evilCallback);
  3491.   }
  3492. }
  3493.  
  3494. /** 
  3495.  * Callback from whitelist check when remote lookups is on.
  3496.  * @param url String url to lookup
  3497.  * @param request nsIRequest object
  3498.  * @param status int enum from callback (PROT_ListWarden.IN_BLACKLIST,
  3499.  *    PROT_ListWarden.IN_WHITELIST, PROT_ListWarden.NOT_FOUND)
  3500.  */
  3501. PROT_PhishingWarden.prototype.maybeMakeRemoteCheck_ = function(url, request, status) {
  3502.   if (PROT_ListWarden.IN_WHITELIST == status)
  3503.     return;
  3504.  
  3505.   G_Debug(this, "Local whitelist lookup failed");
  3506.   this.fetcher_.get(url,
  3507.                     BindToObject(this.onTRFetchComplete,
  3508.                                  this,
  3509.                                  url,
  3510.                                  request));
  3511. }
  3512.  
  3513. /**
  3514.  * Invoked with the result of a lookupserver request.
  3515.  *
  3516.  * @param url String the URL we looked up
  3517.  * @param request The nsIRequest in which we're interested
  3518.  * @param trValues Object holding name/value pairs parsed from the
  3519.  *                 lookupserver's response
  3520.  * @param status Number HTTP status code or NS_ERROR_NOT_AVAILABLE if there's
  3521.  *               an HTTP error
  3522.  */
  3523. PROT_PhishingWarden.prototype.onTRFetchComplete = function(url,
  3524.                                                            request,
  3525.                                                            trValues,
  3526.                                                            status) {
  3527.   // Did the remote http request succeed?  If not, we fall back on
  3528.   // local lists.
  3529.   if (status == Components.results.NS_ERROR_NOT_AVAILABLE ||
  3530.       this.requestBackoff_.isErrorStatus_(status)) {
  3531.     this.requestBackoff_.noteServerResponse(status);
  3532.  
  3533.     G_Debug(this, "remote check failed, using local lists instead");
  3534.     var evilCallback = BindToObject(this.localListMatch_,
  3535.                                     this,
  3536.                                     url,
  3537.                                     request);
  3538.     this.isEvilURL(url, evilCallback);
  3539.   } else {
  3540.     var callback = BindToObject(this.houstonWeHaveAProblem_, this, request);
  3541.     this.checkRemoteData(callback, trValues);
  3542.   }
  3543. }
  3544.  
  3545. /**
  3546.  * One of our Check* methods found a problem with a request. Why do we
  3547.  * need to keep the nsIRequest (instead of just passing in the URL)? 
  3548.  * Because we need to know when to stop looking for the URL its
  3549.  * fetching, and to know this we need the nsIRequest.isPending flag.
  3550.  *
  3551.  * @param request nsIRequest that is problematic
  3552.  */
  3553. PROT_PhishingWarden.prototype.houstonWeHaveAProblem_ = function(request) {
  3554.  
  3555.   // We have a problem request that might or might not be associated
  3556.   // with a Document that's currently in a browser. If it is, we 
  3557.   // want that Document. If it's not, we want to give it a chance to 
  3558.   // be loaded. See below for complete details.
  3559.  
  3560.   if (this.maybeLocateProblem_(request))       // Cases 1 and 2 (see below)
  3561.     return;
  3562.  
  3563.   // OK, so the request isn't associated with any currently accessible
  3564.   // Document, and we want to give it the chance to be. We don't want
  3565.   // to retry forever (e.g., what if the Document was already displayed
  3566.   // and navigated away from?), so we'll use nsIRequest.isPending to help
  3567.   // us decide what to do.
  3568.   //
  3569.   // Aácomplication arises because there is a lag between when a
  3570.   // request transitions from pending to not-pending and when it's
  3571.   // associated with a Document in a browser. The transition from
  3572.   // pending to not occurs just before the notification corresponding
  3573.   // to NavWatcher.DOCNAVSTART (see NavWatcher), but the association
  3574.   // occurs afterwards. Unfortunately, we're probably in DOCNAVSTART.
  3575.   // 
  3576.   // Diagnosis by Darin:
  3577.   // ---------------------------------------------------------------------------
  3578.   // Here's a summary of what happens:
  3579.   //
  3580.   //   RestorePresentation() {
  3581.   //     Dispatch_OnStateChange(dummy_request, STATE_START)
  3582.   //     PostCompletionEvent()
  3583.   //   }
  3584.   //
  3585.   //   CompletionEvent() {
  3586.   //     ReallyRestorePresentation()
  3587.   //     Dispatch_OnStateChange(dummy_request, STATE_STOP)
  3588.   //   }
  3589.   //
  3590.   // So, now your code receives that initial OnStateChange event and sees
  3591.   // that the dummy_request is not pending and not loaded in any window.
  3592.   // So, you put a timeout(0) event in the queue.  Then, the CompletionEvent
  3593.   // is added to the queue.  The stack unwinds....
  3594.   //
  3595.   // Your timeout runs, and you find that the dummy_request is still not
  3596.   // pending and not loaded in any window.  Then the CompletionEvent
  3597.   // runs, and it hooks up the cached presentation.
  3598.   // 
  3599.   // https://bugzilla.mozilla.org/show_bug.cgi?id=319527
  3600.   // ---------------------------------------------------------------------------
  3601.   //
  3602.   // So the logic is:
  3603.   //
  3604.   //         request     found an unhandled          
  3605.   //  case   pending?    doc with the url?         action
  3606.   //  ----------------------------------------------------------------
  3607.   //   1      yes             yes           Use that doc (handled above)
  3608.   //   2      no              yes           Use that doc (handled above)
  3609.   //   3      yes             no            Retry
  3610.   //   4      no              no            Retry twice (case described above)
  3611.   //
  3612.   // We don't get into trouble with Docs with the same URL "stealing" the 
  3613.   // warning because there is exactly one warning signaled per nav to 
  3614.   // a problem URL, and each Doc can be marked as problematic at most once.
  3615.  
  3616.   if (request.isPending()) {        // Case 3
  3617.  
  3618.     G_Debug(this, "Can't find problem Doc; Req pending. Retrying.");
  3619.     new G_Alarm(BindToObject(this.houstonWeHaveAProblem_, 
  3620.                              this, 
  3621.                              request), 
  3622.                 200 /*ms*/);
  3623.  
  3624.   } else {                          // Case 4
  3625.  
  3626.     G_Debug(this, 
  3627.             "Can't find problem Doc; Req completed. Retrying at most twice.");
  3628.     new G_ConditionalAlarm(BindToObject(this.maybeLocateProblem_, 
  3629.                                         this, 
  3630.                                         request),
  3631.                            0 /* next event loop */, 
  3632.                            true /* repeat */, 
  3633.                            2 /* at most twice */);
  3634.   }
  3635. }
  3636.  
  3637. /**
  3638.  * Query all browser views we know about and offer them the chance to
  3639.  * handle the problematic request.
  3640.  *
  3641.  * @param request nsIRequest that is problematic
  3642.  * 
  3643.  * @returns Boolean indicating if someone decided to handle it
  3644.  */
  3645. PROT_PhishingWarden.prototype.maybeLocateProblem_ = function(request) {
  3646.   G_Debug(this, "Trying to find the problem.");
  3647.  
  3648.   G_Debug(this, this.browserViews_.length + " browser views to check.");
  3649.   for (var i = 0; i < this.browserViews_.length; i++) {
  3650.     if (this.browserViews_[i].tryToHandleProblemRequest(this, request)) {
  3651.       G_Debug(this, "Found browser view willing to handle problem!");
  3652.       return true;
  3653.     }
  3654.     G_Debug(this, "wrong browser view");
  3655.   }
  3656.   return false;
  3657. }
  3658.  
  3659. /**
  3660.  * Indicates if this URL is one of the possible blacklist test URLs.
  3661.  * These test URLs should always be considered as phishy.
  3662.  *
  3663.  * @param url URL to check 
  3664.  * @return A boolean indicating whether this is one of our blacklist
  3665.  *         test URLs
  3666.  */
  3667. PROT_PhishingWarden.prototype.isBlacklistTestURL = function(url) {
  3668.   // Explicitly check for URL so we don't get JS warnings in strict mode.
  3669.   if (kTestUrls[url])
  3670.     return true;
  3671.   return false;
  3672. }
  3673.  
  3674. /**
  3675.  * Callback for found local blacklist match.  First we report that we have
  3676.  * a blacklist hit, then we bring up the warning dialog.
  3677.  * @param status Number enum from callback (PROT_ListWarden.IN_BLACKLIST,
  3678.  *    PROT_ListWarden.IN_WHITELIST, PROT_ListWarden.NOT_FOUND)
  3679.  */
  3680. PROT_PhishingWarden.prototype.localListMatch_ = function(url, request, status) {
  3681.   if (PROT_ListWarden.IN_BLACKLIST != status)
  3682.     return;
  3683.  
  3684.   // Maybe send a report
  3685.   (new PROT_Reporter).report("phishblhit", url);
  3686.   this.houstonWeHaveAProblem_(request);
  3687. }
  3688.  
  3689. /**
  3690.  * Examine data fetched from a lookup server for evidence of a
  3691.  * phishing problem. 
  3692.  *
  3693.  * @param callback Function to invoke if there is a problem. 
  3694.  * @param trValues Object containing name/value pairs the server returned
  3695.  */
  3696. PROT_PhishingWarden.prototype.checkRemoteData = function(callback, 
  3697.                                                          trValues) {
  3698.  
  3699.   if (!trValues) {
  3700.     G_Debug(this, "Didn't get TR values from the server.");
  3701.     return;
  3702.   }
  3703.   
  3704.   G_Debug(this, "Page has phishiness " + trValues["phishy"]);
  3705.  
  3706.   if (trValues["phishy"] == 1) {     // It's on our blacklist 
  3707.     G_Debug(this, "Remote blacklist hit");
  3708.     callback(this);
  3709.   } else {
  3710.     G_Debug(this, "Remote blacklist miss");
  3711.   }
  3712. }
  3713.  
  3714. /* ***** BEGIN LICENSE BLOCK *****
  3715.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3716.  *
  3717.  * The contents of this file are subject to the Mozilla Public License Version
  3718.  * 1.1 (the "License"); you may not use this file except in compliance with
  3719.  * the License. You may obtain a copy of the License at
  3720.  * http://www.mozilla.org/MPL/
  3721.  *
  3722.  * Software distributed under the License is distributed on an "AS IS" basis,
  3723.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3724.  * for the specific language governing rights and limitations under the
  3725.  * License.
  3726.  *
  3727.  * The Original Code is Google Safe Browsing.
  3728.  *
  3729.  * The Initial Developer of the Original Code is Google Inc.
  3730.  * Portions created by the Initial Developer are Copyright (C) 2006
  3731.  * the Initial Developer. All Rights Reserved.
  3732.  *
  3733.  * Contributor(s):
  3734.  *   Fritz Schneider <fritz@google.com> (original author)
  3735.  *
  3736.  * Alternatively, the contents of this file may be used under the terms of
  3737.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3738.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3739.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3740.  * of those above. If you wish to allow use of your version of this file only
  3741.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3742.  * use your version of this file under the terms of the MPL, indicate your
  3743.  * decision by deleting the provisions above and replace them with the notice
  3744.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3745.  * the provisions above, a recipient may use your version of this file under
  3746.  * the terms of any one of the MPL, the GPL or the LGPL.
  3747.  *
  3748.  * ***** END LICENSE BLOCK ***** */
  3749.  
  3750.  
  3751. // A tiny class to do reporting for us. We report interesting user actions
  3752. // such as the user hitting a blacklisted page, and the user accepting
  3753. // or declining the warning.
  3754. //
  3755. // Each report has a subject and data. Current reports are:
  3756. //
  3757. // subject         data     meaning
  3758. // --------------------------------
  3759. // phishnavaway    url      the user navigated away from a phishy page
  3760. // phishdecline    url      the user declined our warning
  3761. // phishaccept     url      the user accepted our warning
  3762. // phishblhit      url      the user loaded a phishing page
  3763. //
  3764. // We only send reports in advanced protection mode, and even then we
  3765. // strip cookies from the request before sending it.
  3766.  
  3767. /**
  3768.  * A very complicated class to send pings to the provider. The class does
  3769.  * nothing if we're not in advanced protection mode.
  3770.  *
  3771.  * @constructor
  3772.  */
  3773. function PROT_Reporter() {
  3774.   this.debugZone = "reporter";
  3775.   this.prefs_ = new G_Preferences();
  3776. }
  3777.  
  3778. /**
  3779.  * Send a report!
  3780.  *
  3781.  * @param subject String indicating what this report is about (will be 
  3782.  *                urlencoded)
  3783.  * @param data String giving extra information about this report (will be 
  3784.  *                urlencoded)
  3785.  */
  3786. PROT_Reporter.prototype.report = function(subject, data) {
  3787.   // Send a report iff we're in advanced protection mode
  3788.   if (!this.prefs_.getPref(kPhishWardenRemoteLookups, false))
  3789.     return;
  3790.   // Make sure a report url is defined
  3791.   var url = gDataProvider.getReportURL();
  3792.  
  3793.   // Report url is optional, so we just ignore the request if a report
  3794.   // url isn't provided.
  3795.   if (!url)
  3796.     return;
  3797.  
  3798.   url += "evts=" + encodeURIComponent(subject)
  3799.          + "&evtd=" + encodeURIComponent(data);
  3800.   G_Debug(this, "Sending report: " + url);
  3801.   (new PROT_XMLFetcher(true /* strip cookies */)).get(url, null /* no cb */);
  3802. }
  3803. /* ***** BEGIN LICENSE BLOCK *****
  3804.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3805.  *
  3806.  * The contents of this file are subject to the Mozilla Public License Version
  3807.  * 1.1 (the "License"); you may not use this file except in compliance with
  3808.  * the License. You may obtain a copy of the License at
  3809.  * http://www.mozilla.org/MPL/
  3810.  *
  3811.  * Software distributed under the License is distributed on an "AS IS" basis,
  3812.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  3813.  * for the specific language governing rights and limitations under the
  3814.  * License.
  3815.  *
  3816.  * The Original Code is Google Safe Browsing.
  3817.  *
  3818.  * The Initial Developer of the Original Code is Google Inc.
  3819.  * Portions created by the Initial Developer are Copyright (C) 2006
  3820.  * the Initial Developer. All Rights Reserved.
  3821.  *
  3822.  * Contributor(s):
  3823.  *   Fritz Schneider <fritz@google.com> (original author)
  3824.  *
  3825.  * Alternatively, the contents of this file may be used under the terms of
  3826.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  3827.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  3828.  * in which case the provisions of the GPL or the LGPL are applicable instead
  3829.  * of those above. If you wish to allow use of your version of this file only
  3830.  * under the terms of either the GPL or the LGPL, and not to allow others to
  3831.  * use your version of this file under the terms of the MPL, indicate your
  3832.  * decision by deleting the provisions above and replace them with the notice
  3833.  * and other provisions required by the GPL or the LGPL. If you do not delete
  3834.  * the provisions above, a recipient may use your version of this file under
  3835.  * the terms of any one of the MPL, the GPL or the LGPL.
  3836.  *
  3837.  * ***** END LICENSE BLOCK ***** */
  3838.  
  3839.  
  3840. // A helper class that does "trustrank" lookups on a remote
  3841. // server. Right now this lookup just indicates if a page is
  3842. // phishing. The response format is protocol4 name/value pairs.
  3843. // 
  3844. // Since we're sending full URLs to the server, we try to encrypt
  3845. // them before transmission. Else HTTPS query params could leak.
  3846.  
  3847. /**
  3848.  * A helper class that fetches trustrank values, parses them, and
  3849.  * passes them via an object to a callback.
  3850.   * 
  3851.  * @constructor
  3852.  */
  3853. function PROT_TRFetcher(opt_noCrypto) {
  3854.   this.debugZone = "trfetcher";
  3855.   this.useCrypto_ = !opt_noCrypto;
  3856.   this.protocol4Parser_ = new G_Protocol4Parser();
  3857.  
  3858.   // We lazily instantiate the UrlCrypto object due to:
  3859.   // https://bugzilla.mozilla.org/show_bug.cgi?id=321024
  3860.   //
  3861.   // Otherwise here we would use:
  3862.   // this.urlCrypto_ = new PROT_UrlCrypto();
  3863. }
  3864.  
  3865. PROT_TRFetcher.TRY_REKEYING_RESPONSE = "pleaserekey";
  3866.  
  3867. /**
  3868.  * Get the URL of the request that will fetch us TR for the argument URL
  3869.  *
  3870.  * @param url String containing the URL we'd like to fetch info about
  3871.  *
  3872.  * @returns String containing the url we should use to fetch tr info
  3873.  */
  3874. PROT_TRFetcher.prototype.getRequestURL_ = function(url) {
  3875.  
  3876.   if (!this.urlCrypto_)
  3877.     this.urlCrypto_ = new PROT_UrlCrypto();
  3878.  
  3879.   G_Debug(this, "Fetching for " + url);
  3880.     
  3881.   var requestURL = gDataProvider.getLookupURL();
  3882.   if (!requestURL)
  3883.     return null;
  3884.  
  3885.   if (this.useCrypto_) {
  3886.     var maybeCryptedParams = this.urlCrypto_.maybeCryptParams({ "q": url});
  3887.     
  3888.     for (var param in maybeCryptedParams) 
  3889.       requestURL += param + "=" + 
  3890.                     encodeURIComponent(maybeCryptedParams[param]) + "&";
  3891.   } else {
  3892.     requestURL += "q=" + encodeURIComponent(url);
  3893.   }
  3894.  
  3895.   G_Debug(this, "Request URL: " + requestURL);
  3896.  
  3897.   return requestURL;
  3898. };
  3899.  
  3900. /**
  3901.  * Fetches information about a page.
  3902.  * 
  3903.  * @param forPage URL for which to fetch info
  3904.  *
  3905.  * @param callback Function to call back when complete.
  3906.  */
  3907. PROT_TRFetcher.prototype.get = function(forPage, callback) {
  3908.   
  3909.   var url = this.getRequestURL_(forPage);
  3910.   if (!url) {
  3911.     G_Debug(this, "No remote lookup url.");
  3912.     return;
  3913.   }
  3914.   var closure = BindToObject(this.onFetchComplete_, this, callback);
  3915.   (new PROT_XMLFetcher()).get(url, closure);
  3916. };
  3917.  
  3918. /**
  3919.  * Invoked when a fetch has completed.
  3920.  *
  3921.  * @param callback Function to invoke with parsed response object
  3922.  * @param responseText Text of the protocol4 message
  3923.  * @param httpStatus Number HTTP status code or NS_ERROR_NOT_AVAILABLE if the
  3924.  *     request failed
  3925.  */
  3926. PROT_TRFetcher.prototype.onFetchComplete_ = function(callback, responseText,
  3927.                                                      httpStatus) {
  3928.   
  3929.   var responseObj = this.extractResponse_(responseText);
  3930.  
  3931.   // The server might tell us to rekey, for example if it sees that
  3932.   // our request was unencrypted (meaning that we might not yet have
  3933.   // a key). If so, pass this hint along to the crypto key manager.
  3934.  
  3935.   if (responseObj[PROT_TRFetcher.TRY_REKEYING_RESPONSE] == "1" &&
  3936.       this.urlCrypto_) {
  3937.     G_Debug(this, "We're supposed to re-key. Trying.");
  3938.     var manager = this.urlCrypto_.getManager();
  3939.     if (manager)
  3940.       manager.maybeReKey();
  3941.   }
  3942.  
  3943.   G_Debug(this, "TR Response:");
  3944.   for (var field in responseObj)
  3945.     G_Debug(this, field + "=" + responseObj[field]);
  3946.  
  3947.   callback(responseObj, httpStatus);
  3948. };
  3949.  
  3950. /**
  3951.  * Parse a protocol4 message (lookup server response)
  3952.  * 
  3953.  * @param responseText String containing the server's response
  3954.  *
  3955.  * @returns Object containing the returned values or null if no
  3956.  *          response was received
  3957.  */
  3958. PROT_TRFetcher.prototype.extractResponse_ = function(responseText) {
  3959.   return this.protocol4Parser_.parse(responseText);
  3960. };
  3961.  
  3962. //@line 27 "/cygdrive/K/tinderbuild/src/flock/mozilla/browser/components/safebrowsing/src/nsSafebrowsingApplication.js"
  3963.  
  3964. var modScope = this;
  3965. function Init() {
  3966.   var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
  3967.               .getService().wrappedJSObject;
  3968.   modScope.String.prototype.startsWith = jslib.String.prototype.startsWith;
  3969.   modScope.G_Debug = jslib.G_Debug;
  3970.   modScope.G_Assert = jslib.G_Assert;
  3971.   modScope.G_Alarm = jslib.G_Alarm;
  3972.   modScope.G_ConditionalAlarm = jslib.G_ConditionalAlarm;
  3973.   modScope.G_ObserverWrapper = jslib.G_ObserverWrapper;
  3974.   modScope.G_Preferences = jslib.G_Preferences;
  3975.   modScope.PROT_XMLFetcher = jslib.PROT_XMLFetcher;
  3976.   modScope.BindToObject = jslib.BindToObject;
  3977.   modScope.G_Protocol4Parser = jslib.G_Protocol4Parser;
  3978.   modScope.G_ObjectSafeMap = jslib.G_ObjectSafeMap;
  3979.   modScope.PROT_UrlCrypto = jslib.PROT_UrlCrypto;
  3980.   modScope.RequestBackoff = jslib.RequestBackoff;
  3981.   
  3982.   // We only need to call Init once
  3983.   modScope.Init = function() {};
  3984. }
  3985.  
  3986. // Module object
  3987. function SafebrowsingApplicationMod() {
  3988.   this.firstTime = true;
  3989.   this.cid = Components.ID("{c64d0bcb-8270-4ca7-a0b3-3380c8ffecb5}");
  3990.   this.progid = "@mozilla.org/safebrowsing/application;1";
  3991. }
  3992.  
  3993. SafebrowsingApplicationMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
  3994.   if (this.firstTime) {
  3995.     this.firstTime = false;
  3996.     throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  3997.   }
  3998.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  3999.   compMgr.registerFactoryLocation(this.cid,
  4000.                                   "Safebrowsing Application Module",
  4001.                                   this.progid,
  4002.                                   fileSpec,
  4003.                                   loc,
  4004.                                   type);
  4005. };
  4006.  
  4007. SafebrowsingApplicationMod.prototype.getClassObject = function(compMgr, cid, iid) {  
  4008.   if (!cid.equals(this.cid))
  4009.     throw Components.results.NS_ERROR_NO_INTERFACE;
  4010.   if (!iid.equals(Ci.nsIFactory))
  4011.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  4012.  
  4013.   return this.factory;
  4014. }
  4015.  
  4016. SafebrowsingApplicationMod.prototype.canUnload = function(compMgr) {
  4017.   return true;
  4018. }
  4019.  
  4020. SafebrowsingApplicationMod.prototype.factory = {
  4021.   createInstance: function(outer, iid) {
  4022.     if (outer != null)
  4023.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  4024.     Init();
  4025.     return new PROT_Application();
  4026.   }
  4027. };
  4028.  
  4029. var ApplicationModInst = new SafebrowsingApplicationMod();
  4030.  
  4031. function NSGetModule(compMgr, fileSpec) {
  4032.   return ApplicationModInst;
  4033. }
  4034.